GnuCash 2.4.99
gnc-ab-utils.c
00001 /*
00002  * gnc-ab-utils.c --
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of
00007  * the License, or (at your option) any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, contact:
00016  *
00017  * Free Software Foundation           Voice:  +1-617-542-5942
00018  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
00019  * Boston, MA  02110-1301,  USA       gnu@gnu.org
00020  */
00021 
00030 #include "config.h"
00031 
00032 #include <glib/gi18n.h>
00033 #include <gwenhywfar/gwenhywfar.h>
00034 #include <aqbanking/banking.h>
00035 
00036 #include "window-reconcile.h"
00037 #include "Transaction.h"
00038 #include "dialog-ab-trans.h"
00039 #include "gnc-ab-kvp.h"
00040 #include "gnc-ab-utils.h"
00041 #include "gnc-gconf-utils.h"
00042 #include "gnc-glib-utils.h"
00043 #include "gnc-gwen-gui.h"
00044 #include "gnc-ui.h"
00045 #include "import-account-matcher.h"
00046 #include "import-main-matcher.h"
00047 #include "import-utilities.h"
00048 #include "qof.h"
00049 
00050 #ifdef AQBANKING_VERSION_5_PLUS
00051 # include <aqbanking/abgui.h>
00052 #endif /* AQBANKING_VERSION_5_PLUS */
00053 
00054 /* This static indicates the debugging module that this .o belongs to.  */
00055 static QofLogModule log_module = G_LOG_DOMAIN;
00056 
00057 /* Global variables for AB_BANKING caching. */
00058 static AB_BANKING *gnc_AB_BANKING = NULL;
00059 static gint gnc_AB_BANKING_refcount = 0;
00060 
00061 static gpointer join_ab_strings_cb(const gchar *str, gpointer user_data);
00062 static Account *gnc_ab_accinfo_to_gnc_acc(
00063     AB_IMEXPORTER_ACCOUNTINFO *account_info);
00064 static const AB_TRANSACTION *txn_transaction_cb(
00065     const AB_TRANSACTION *element, gpointer user_data);
00066 static AB_IMEXPORTER_ACCOUNTINFO *txn_accountinfo_cb(
00067     AB_IMEXPORTER_ACCOUNTINFO *element, gpointer user_data);
00068 static AB_IMEXPORTER_ACCOUNTINFO *bal_accountinfo_cb(
00069     AB_IMEXPORTER_ACCOUNTINFO *element, gpointer user_data);
00070 
00071 struct _GncABImExContextImport
00072 {
00073     guint awaiting;
00074     gboolean txn_found;
00075     Account *gnc_acc;
00076     AB_ACCOUNT *ab_acc;
00077     gboolean execute_txns;
00078     AB_BANKING *api;
00079     GtkWidget *parent;
00080     AB_JOB_LIST2 *job_list;
00081     GNCImportMainMatcher *generic_importer;
00082     GData *tmp_job_list;
00083 };
00084 
00085 void
00086 gnc_GWEN_Init(void)
00087 {
00088     gint i;
00089     gchar* gwen_logging = g_strdup(g_getenv("GWEN_LOGLEVEL"));
00090     gchar* aqb_logging = g_strdup(g_getenv("AQBANKING_LOGLEVEL"));
00091 
00092     /* Initialize gwen library */
00093     GWEN_Init();
00094 
00095     /* Initialize gwen logging */
00096     if (gnc_gconf_get_bool(GCONF_SECTION_AQBANKING, KEY_VERBOSE_DEBUG, NULL))
00097     {
00098         if (!gwen_logging)
00099         {
00100             GWEN_Logger_SetLevel(NULL, GWEN_LoggerLevel_Info);
00101             GWEN_Logger_SetLevel(GWEN_LOGDOMAIN, GWEN_LoggerLevel_Info);
00102         }
00103         if (!aqb_logging)
00104             GWEN_Logger_SetLevel(AQBANKING_LOGDOMAIN, GWEN_LoggerLevel_Debug);
00105     }
00106     else
00107     {
00108         if (!gwen_logging)
00109         {
00110             GWEN_Logger_SetLevel(NULL, GWEN_LoggerLevel_Error);
00111             GWEN_Logger_SetLevel(GWEN_LOGDOMAIN, GWEN_LoggerLevel_Error);
00112         }
00113         if (!aqb_logging)
00114             GWEN_Logger_SetLevel(AQBANKING_LOGDOMAIN, GWEN_LoggerLevel_Warning);
00115     }
00116     g_free(gwen_logging);
00117     g_free(aqb_logging);
00118     gnc_GWEN_Gui_log_init();
00119 }
00120 
00121 void
00122 gnc_GWEN_Fini(void)
00123 {
00124     /* Shutdown the GWEN_GUIs */
00125     gnc_GWEN_Gui_shutdown();
00126     GWEN_Logger_SetLevel(NULL, GWEN_LoggerLevel_Error);
00127     GWEN_Logger_SetLevel(GWEN_LOGDOMAIN, GWEN_LoggerLevel_Warning);
00128     GWEN_Logger_SetLevel(AQBANKING_LOGDOMAIN, GWEN_LoggerLevel_Warning);
00129 
00130     /* Finalize gwen library */
00131     GWEN_Fini();
00132 }
00133 
00134 static GWEN_GUI *gnc_gwengui_extended_by_ABBanking;
00135 
00136 AB_BANKING *
00137 gnc_AB_BANKING_new(void)
00138 {
00139     AB_BANKING *api;
00140 
00141     if (gnc_AB_BANKING)
00142     {
00143         /* API cached. */
00144         api = gnc_AB_BANKING;
00145 
00146         /* Init the API again. */
00147         if (gnc_AB_BANKING_refcount == 0)
00148             g_return_val_if_fail(AB_Banking_Init(api) == 0, NULL);
00149 
00150     }
00151     else
00152     {
00153         api = AB_Banking_new("gnucash", NULL, 0);
00154         g_return_val_if_fail(api, NULL);
00155 
00156 #ifdef AQBANKING_VERSION_4_PLUS
00157         /* Check for config migration */
00158         if (AB_Banking_HasConf4(api
00159 # ifndef AQBANKING_VERSION_5_PLUS
00160                                 , 0
00161 # endif
00162                                ) != 0)
00163         {
00164             if (AB_Banking_HasConf3(api
00165 # ifndef AQBANKING_VERSION_5_PLUS
00166                                     , 0
00167 # endif
00168                                    ) == 0)
00169             {
00170                 g_message("gnc_AB_BANKING_new: importing aqbanking3 configuration\n");
00171                 if (AB_Banking_ImportConf3(api
00172 # ifndef AQBANKING_VERSION_5_PLUS
00173                                            , 0
00174 # endif
00175                                           ) < 0)
00176                 {
00177                     g_message("gnc_AB_BANKING_new: unable to import aqbanking3 configuration\n");
00178                 }
00179             }
00180             else if (AB_Banking_HasConf2(api
00181 # ifndef AQBANKING_VERSION_5_PLUS
00182                                          , 0
00183 # endif
00184                                         ) == 0)
00185             {
00186                 g_message("gnc_AB_BANKING_new: importing aqbanking2 configuration\n");
00187                 if (AB_Banking_ImportConf2(api
00188 # ifndef AQBANKING_VERSION_5_PLUS
00189                                            , 0
00190 # endif
00191                                           ) < 0)
00192                 {
00193                     g_message("gnc_AB_BANKING_new: unable to import aqbanking2 configuration\n");
00194                 }
00195             }
00196         }
00197 #endif /* AQBANKING_VERSION_4_PLUS */
00198 
00199         /* Init the API */
00200         g_return_val_if_fail(AB_Banking_Init(api) == 0, NULL);
00201 
00202 #ifdef AQBANKING_VERSION_5_PLUS
00203         gnc_gwengui_extended_by_ABBanking = GWEN_Gui_GetGui();
00204         AB_Gui_Extend(gnc_gwengui_extended_by_ABBanking, api);
00205 #endif /* AQBANKING_VERSION_5_PLUS */
00206 
00207         /* Cache it */
00208         gnc_AB_BANKING = api;
00209         gnc_AB_BANKING_refcount = 0;
00210     }
00211 
00212     gnc_AB_BANKING_refcount++;
00213 
00214     return api;
00215 }
00216 
00217 void
00218 gnc_AB_BANKING_delete(AB_BANKING *api)
00219 {
00220     if (!api)
00221         api = gnc_AB_BANKING;
00222 
00223     if (api)
00224     {
00225         if (api == gnc_AB_BANKING)
00226         {
00227             gnc_AB_BANKING = NULL;
00228             gnc_AB_BANKING_fini(api);
00229         }
00230 
00231         AB_Banking_free(api);
00232     }
00233 }
00234 
00235 
00236 gint
00237 gnc_AB_BANKING_fini(AB_BANKING *api)
00238 {
00239     if (api == gnc_AB_BANKING)
00240     {
00241         if (--gnc_AB_BANKING_refcount == 0)
00242         {
00243 #ifdef AQBANKING_VERSION_5_PLUS
00244             if (gnc_gwengui_extended_by_ABBanking)
00245                 AB_Gui_Unextend(gnc_gwengui_extended_by_ABBanking);
00246             gnc_gwengui_extended_by_ABBanking = NULL;
00247 #endif /* AQBANKING_VERSION_5_PLUS */
00248             return AB_Banking_Fini(api);
00249         }
00250     }
00251     else
00252     {
00253 #ifdef AQBANKING_VERSION_5_PLUS
00254         if (gnc_gwengui_extended_by_ABBanking)
00255             AB_Gui_Unextend(gnc_gwengui_extended_by_ABBanking);
00256         gnc_gwengui_extended_by_ABBanking = NULL;
00257 #endif /* AQBANKING_VERSION_5_PLUS */
00258         return AB_Banking_Fini(api);
00259     }
00260     return 0;
00261 }
00262 
00263 AB_ACCOUNT *
00264 gnc_ab_get_ab_account(const AB_BANKING *api, Account *gnc_acc)
00265 {
00266     AB_ACCOUNT *ab_account = NULL;
00267     const gchar *bankcode = NULL;
00268     const gchar *accountid = NULL;
00269     guint32 account_uid = 0;
00270 
00271     bankcode = gnc_ab_get_account_bankcode(gnc_acc);
00272     accountid = gnc_ab_get_account_accountid(gnc_acc);
00273     account_uid = gnc_ab_get_account_uid (gnc_acc);
00274 
00275     if (account_uid > 0)
00276     {
00277         ab_account = AB_Banking_GetAccount(api, account_uid);
00278 
00279         if (!ab_account && bankcode && *bankcode && accountid && *accountid)
00280         {
00281             g_message("gnc_ab_get_ab_account: No AB_ACCOUNT found for UID %d, "
00282                       "trying bank code\n", account_uid);
00283             ab_account = AB_Banking_GetAccountByCodeAndNumber(api, bankcode,
00284                          accountid);
00285         }
00286         return ab_account;
00287 
00288     }
00289     else if (bankcode && *bankcode && accountid && *accountid)
00290     {
00291         ab_account = AB_Banking_GetAccountByCodeAndNumber(api, bankcode,
00292                      accountid);
00293         return ab_account;
00294     }
00295 
00296     return NULL;
00297 }
00298 
00299 gchar *
00300 gnc_AB_VALUE_to_readable_string(const AB_VALUE *value)
00301 {
00302     if (value)
00303         return g_strdup_printf("%.2f %s",
00304                                AB_Value_GetValueAsDouble(value),
00305                                AB_Value_GetCurrency(value));
00306     else
00307         return g_strdup_printf("%.2f", 0.0);
00308 }
00309 
00314 static gpointer
00315 join_ab_strings_cb(const gchar *str, gpointer user_data)
00316 {
00317     gchar **acc = user_data;
00318     gchar *tmp;
00319 
00320     if (!str || !*str)
00321         return NULL;
00322 
00323     tmp = g_strdup(str);
00324     g_strstrip(tmp);
00325     gnc_utf8_strip_invalid(tmp);
00326 
00327     if (*acc)
00328     {
00329         gchar *join = g_strjoin(" ", *acc, tmp, (gchar*) NULL);
00330         g_free(*acc);
00331         g_free(tmp);
00332         *acc = join;
00333     }
00334     else
00335     {
00336         *acc = tmp;
00337     }
00338     return NULL;
00339 }
00340 
00341 gchar *
00342 gnc_ab_get_remote_name(const AB_TRANSACTION *ab_trans)
00343 {
00344     const GWEN_STRINGLIST *ab_remote_name;
00345     gchar *gnc_other_name = NULL;
00346 
00347     g_return_val_if_fail(ab_trans, NULL);
00348 
00349     ab_remote_name = AB_Transaction_GetRemoteName(ab_trans);
00350     if (ab_remote_name)
00351         GWEN_StringList_ForEach(ab_remote_name, join_ab_strings_cb,
00352                                 &gnc_other_name);
00353 
00354     if (!gnc_other_name || !*gnc_other_name)
00355     {
00356         g_free(gnc_other_name);
00357         gnc_other_name = NULL;
00358     }
00359 
00360     return gnc_other_name;
00361 }
00362 
00363 gchar *
00364 gnc_ab_get_purpose(const AB_TRANSACTION *ab_trans)
00365 {
00366     const GWEN_STRINGLIST *ab_purpose;
00367     gchar *gnc_description = NULL;
00368 
00369     g_return_val_if_fail(ab_trans, g_strdup(""));
00370 
00371     ab_purpose = AB_Transaction_GetPurpose(ab_trans);
00372     if (ab_purpose)
00373         GWEN_StringList_ForEach(ab_purpose, join_ab_strings_cb,
00374                                 &gnc_description);
00375 
00376     if (!gnc_description)
00377         gnc_description = g_strdup("");
00378 
00379     return gnc_description;
00380 }
00381 
00382 gchar *
00383 gnc_ab_description_to_gnc(const AB_TRANSACTION *ab_trans)
00384 {
00385     /* Description */
00386     gchar *description = gnc_ab_get_purpose(ab_trans);
00387     gchar *other_name = gnc_ab_get_remote_name(ab_trans);
00388     gchar *retval;
00389 
00390     if (other_name)
00391     {
00392         if (description && *description)
00393         {
00394             retval = g_strdup_printf("%s; %s", description, other_name);
00395         }
00396         else
00397         {
00398             retval = g_strdup(other_name);
00399         }
00400     }
00401     else
00402     {
00403         if (description && *description)
00404         {
00405             retval = g_strdup(description);
00406         }
00407         else
00408         {
00409             retval = g_strdup(_("Unspecified"));
00410         }
00411     }
00412     g_free(description);
00413     g_free(other_name);
00414 
00415     return retval;
00416 }
00417 
00418 gchar *
00419 gnc_ab_memo_to_gnc(const AB_TRANSACTION *ab_trans)
00420 {
00421     const gchar *ab_remote_accountnumber =
00422         AB_Transaction_GetRemoteAccountNumber(ab_trans);
00423     const gchar *ab_remote_bankcode =
00424         AB_Transaction_GetRemoteBankCode(ab_trans);
00425 
00426     gchar *ab_other_accountid = g_strdup(ab_remote_accountnumber ? ab_remote_accountnumber : "");
00427     gchar *ab_other_bankcode = g_strdup(ab_remote_bankcode ? ab_remote_bankcode : "");
00428 
00429     gboolean have_accountid;
00430     gboolean have_bankcode;
00431 
00432     gchar *retval;
00433 
00434     /* Ensure string is in utf8 */
00435     gnc_utf8_strip_invalid(ab_other_accountid);
00436     gnc_utf8_strip_invalid(ab_other_bankcode);
00437 
00438     /* and -then- trim it */
00439     g_strstrip(ab_other_accountid);
00440     g_strstrip(ab_other_bankcode);
00441 
00442 
00443     have_accountid = ab_other_accountid && *ab_other_accountid;
00444     have_bankcode = ab_other_bankcode && *ab_other_bankcode;
00445 
00446     if ( have_accountid || have_bankcode )
00447     {
00448         retval = g_strdup_printf("%s %s %s %s",
00449                                  have_accountid ? _("Account") : "",
00450                                  have_accountid ? ab_other_accountid : "",
00451                                  have_bankcode  ? _("Bank") : "",
00452                                  have_bankcode  ? ab_other_bankcode : ""
00453                                 );
00454         g_strstrip(retval);
00455     }
00456     else
00457     {
00458         retval = g_strdup("");
00459     }
00460 
00461     g_free(ab_other_accountid);
00462     g_free(ab_other_bankcode);
00463 
00464     return retval;
00465 }
00466 
00467 Transaction *
00468 gnc_ab_trans_to_gnc(const AB_TRANSACTION *ab_trans, Account *gnc_acc)
00469 {
00470     QofBook *book;
00471     Transaction *gnc_trans;
00472     const gchar *fitid;
00473     const GWEN_TIME *valuta_date;
00474     time_t current_time;
00475     const char *custref;
00476     gchar *description;
00477     Split *split;
00478     gchar *memo;
00479 
00480     g_return_val_if_fail(ab_trans && gnc_acc, NULL);
00481 
00482     /* Create new GnuCash transaction for the given AqBanking one */
00483     book = gnc_account_get_book(gnc_acc);
00484     gnc_trans = xaccMallocTransaction(book);
00485     xaccTransBeginEdit(gnc_trans);
00486 
00487     /* Date / Time */
00488     valuta_date = AB_Transaction_GetValutaDate(ab_trans);
00489     if (!valuta_date)
00490     {
00491         const GWEN_TIME *normal_date = AB_Transaction_GetDate(ab_trans);
00492         if (normal_date)
00493             valuta_date = normal_date;
00494     }
00495     if (valuta_date)
00496         xaccTransSetDatePostedSecs(gnc_trans, GWEN_Time_toTime_t(valuta_date));
00497     else
00498         g_warning("transaction_cb: Oops, date 'valuta_date' was NULL");
00499 
00500     current_time = time(NULL);
00501     xaccTransSetDateEnteredSecs(gnc_trans, mktime(localtime(&current_time)));
00502 
00503     /* Currency.  We take simply the default currency of the gnucash account */
00504     xaccTransSetCurrency(gnc_trans, xaccAccountGetCommodity(gnc_acc));
00505 
00506     /* Number.  We use the "customer reference", if there is one. */
00507     custref = AB_Transaction_GetCustomerReference(ab_trans);
00508     if (custref && *custref
00509             && g_ascii_strncasecmp(custref, "NONREF", 6) != 0)
00510         xaccTransSetNum(gnc_trans, custref);
00511 
00512     /* Description */
00513     description = gnc_ab_description_to_gnc(ab_trans);
00514     xaccTransSetDescription(gnc_trans, description);
00515     g_free(description);
00516 
00517     /* Notes. */
00518     /* xaccTransSetNotes(gnc_trans, g_notes); */
00519     /* But Nobody ever uses the Notes field? */
00520 
00521     /* Add one split */
00522     split = xaccMallocSplit(book);
00523     xaccSplitSetParent(split, gnc_trans);
00524     xaccSplitSetAccount(split, gnc_acc);
00525 
00526     /* Set OFX unique transaction ID */
00527     fitid = AB_Transaction_GetFiId(ab_trans);
00528     if (fitid && *fitid)
00529         gnc_import_set_split_online_id(split, fitid);
00530 
00531     {
00532         /* Amount into the split */
00533         const AB_VALUE *ab_value = AB_Transaction_GetValue(ab_trans);
00534         double d_value = ab_value ? AB_Value_GetValueAsDouble (ab_value) : 0.0;
00535         AB_TRANSACTION_TYPE ab_type = AB_Transaction_GetType (ab_trans);
00536         gnc_numeric gnc_amount;
00537 
00538         /*printf("Transaction with value %f has type %d\n", d_value, ab_type);*/
00539         /* If the value is positive, but the transaction type says the
00540            money is transferred away from our account (Transfer instead of
00541            DebitNote), we switch the value to negative. */
00542         if (d_value > 0.0 && ab_type == AB_Transaction_TypeTransfer)
00543             d_value = -d_value;
00544 
00545         gnc_amount = double_to_gnc_numeric(
00546                          d_value,
00547                          xaccAccountGetCommoditySCU(gnc_acc),
00548                          GNC_HOW_RND_ROUND_HALF_UP);
00549         if (!ab_value)
00550             g_warning("transaction_cb: Oops, value was NULL.  Using 0");
00551         xaccSplitSetBaseValue(split, gnc_amount, xaccAccountGetCommodity(gnc_acc));
00552     }
00553 
00554     /* Memo in the Split. */
00555     memo = gnc_ab_memo_to_gnc(ab_trans);
00556     xaccSplitSetMemo(split, memo);
00557     g_free(memo);
00558 
00559     return gnc_trans;
00560 }
00561 
00569 static Account *
00570 gnc_ab_accinfo_to_gnc_acc(AB_IMEXPORTER_ACCOUNTINFO *acc_info)
00571 {
00572     const gchar *bankcode, *accountnumber;
00573     gchar *online_id;
00574     Account *gnc_acc;
00575 
00576     g_return_val_if_fail(acc_info, NULL);
00577 
00578     bankcode = AB_ImExporterAccountInfo_GetBankCode(acc_info);
00579     accountnumber = AB_ImExporterAccountInfo_GetAccountNumber(acc_info);
00580     online_id = g_strconcat(bankcode ? bankcode : "",
00581                             accountnumber ? accountnumber : "",
00582                             (gchar*)NULL);
00583     gnc_acc = gnc_import_select_account(
00584                   NULL, online_id, 1, AB_ImExporterAccountInfo_GetAccountName(acc_info),
00585                   NULL, ACCT_TYPE_NONE, NULL, NULL);
00586     if (!gnc_acc)
00587     {
00588         g_warning("gnc_ab_accinfo_to_gnc_acc: Could not determine source account"
00589                   " for online_id %s", online_id);
00590     }
00591     g_free(online_id);
00592 
00593     return gnc_acc;
00594 }
00595 
00596 static const AB_TRANSACTION *
00597 txn_transaction_cb(const AB_TRANSACTION *element, gpointer user_data)
00598 {
00599     GncABImExContextImport *data = user_data;
00600     Transaction *gnc_trans;
00601     GncABTransType trans_type;
00602 
00603     g_return_val_if_fail(element && data, NULL);
00604 
00605     /* Create a GnuCash transaction from ab_trans */
00606     gnc_trans = gnc_ab_trans_to_gnc(element, data->gnc_acc);
00607 
00608     if (data->execute_txns && data->ab_acc)
00609     {
00610         AB_TRANSACTION *ab_trans = AB_Transaction_dup(element);
00611         AB_JOB *job;
00612 
00613         /* NEW: The imported transaction has been imported into gnucash.
00614          * Now also add it as a job to aqbanking */
00615         AB_Transaction_SetLocalBankCode(
00616             ab_trans, AB_Account_GetBankCode(data->ab_acc));
00617         AB_Transaction_SetLocalAccountNumber(
00618             ab_trans, AB_Account_GetAccountNumber(data->ab_acc));
00619         AB_Transaction_SetLocalCountry(ab_trans, "DE");
00620 
00621 
00622         switch (AB_Transaction_GetType(ab_trans))
00623         {
00624         case AB_Transaction_TypeDebitNote:
00625             trans_type = SINGLE_DEBITNOTE;
00626             break;
00627         case AB_Transaction_TypeTransaction:
00628             /* trans_type = SINGLE_INTERNAL_TRANSFER;
00629              * break; */
00630         case AB_Transaction_TypeEuTransfer:
00631         case AB_Transaction_TypeTransfer:
00632         default:
00633             trans_type = SINGLE_TRANSFER;
00634         } /* switch */
00635 
00636         job = gnc_ab_get_trans_job(data->ab_acc, ab_trans, trans_type);
00637 
00638         /* Check whether we really got a job */
00639         if (!job || AB_Job_CheckAvailability(job
00640 #ifndef AQBANKING_VERSION_5_PLUS
00641                                              , 0
00642 #endif
00643                                             ))
00644         {
00645             /* Oops, no job, probably not supported by bank */
00646             if (gnc_verify_dialog(
00647                         NULL, FALSE, "%s",
00648                         _("The backend found an error during the preparation "
00649                           "of the job. It is not possible to execute this job. \n"
00650                           "\n"
00651                           "Most probable the bank does not support your chosen "
00652                           "job or your Online Banking account does not have the permission "
00653                           "to execute this job. More error messages might be "
00654                           "visible on your console log.\n"
00655                           "\n"
00656                           "Do you want to enter the job again?")))
00657             {
00658                 gnc_error_dialog(NULL, "Sorry, not implemented yet. Please check the console or trace file logs to see which job was rejected.");
00659             }
00660         }
00661         else
00662         {
00663             gnc_gen_trans_list_add_trans_with_ref_id(data->generic_importer, gnc_trans, AB_Job_GetJobId(job));
00664 
00665             /* AB_Job_List2_PushBack(data->job_list, job); -> delayed until trans is successfully imported */
00666             g_datalist_set_data(&data->tmp_job_list, gnc_AB_JOB_to_readable_string(job), job);
00667         }
00668         AB_Transaction_free(ab_trans);
00669     }
00670     else
00671     {
00672         /* Instead of xaccTransCommitEdit(gnc_trans)  */
00673         gnc_gen_trans_list_add_trans(data->generic_importer, gnc_trans);
00674     }
00675 
00676     return NULL;
00677 }
00678 
00679 static void gnc_ab_trans_processed_cb(GNCImportTransInfo *trans_info,
00680                                       gboolean imported,
00681                                       gpointer user_data)
00682 {
00683     GncABImExContextImport *data = user_data;
00684     gchar *jobname = gnc_AB_JOB_ID_to_string(gnc_import_TransInfo_get_ref_id(trans_info));
00685     AB_JOB *job = g_datalist_get_data(&data->tmp_job_list, jobname);
00686 
00687     if (imported)
00688     {
00689         AB_Job_List2_PushBack(data->job_list, job);
00690     }
00691     else
00692     {
00693         AB_Job_free(job);
00694     }
00695 
00696     g_datalist_remove_data(&data->tmp_job_list, jobname);
00697 }
00698 
00699 gchar *
00700 gnc_AB_JOB_to_readable_string(const AB_JOB *job)
00701 {
00702     if (job)
00703     {
00704         return gnc_AB_JOB_ID_to_string(AB_Job_GetJobId(job));
00705     }
00706     else
00707     {
00708         return gnc_AB_JOB_ID_to_string(0);
00709     }
00710 }
00711 gchar *
00712 gnc_AB_JOB_ID_to_string(gulong job_id)
00713 {
00714     return g_strdup_printf("job_%lu", job_id);
00715 }
00716 
00717 
00718 
00719 static AB_IMEXPORTER_ACCOUNTINFO *
00720 txn_accountinfo_cb(AB_IMEXPORTER_ACCOUNTINFO *element, gpointer user_data)
00721 {
00722     GncABImExContextImport *data = user_data;
00723     Account *gnc_acc;
00724 
00725     g_return_val_if_fail(element && data, NULL);
00726 
00727     if (data->awaiting & IGNORE_TRANSACTIONS)
00728         /* Ignore them */
00729         return NULL;
00730 
00731     if (!AB_ImExporterAccountInfo_GetFirstTransaction(element))
00732         /* No transaction found */
00733         return NULL;
00734     else
00735         data->awaiting |= FOUND_TRANSACTIONS;
00736 
00737     if (!(data->awaiting & AWAIT_TRANSACTIONS))
00738     {
00739         if (gnc_verify_dialog(data->parent, TRUE, "%s",
00740                               _("The bank has sent transaction information "
00741                                 "in its response."
00742                                 "\n"
00743                                 "Do you want to import it?")))
00744         {
00745             data->awaiting |= AWAIT_TRANSACTIONS;
00746         }
00747         else
00748         {
00749             data->awaiting |= IGNORE_TRANSACTIONS;
00750             return NULL;
00751         }
00752     }
00753 
00754     /* Lookup the corresponding gnucash account */
00755     gnc_acc = gnc_ab_accinfo_to_gnc_acc(element);
00756     if (!gnc_acc) return NULL;
00757     data->gnc_acc = gnc_acc;
00758 
00759     if (data->execute_txns)
00760     {
00761         /* Retrieve the aqbanking account that belongs to this gnucash
00762          * account */
00763         data->ab_acc = gnc_ab_get_ab_account(data->api, gnc_acc);
00764         if (!data->ab_acc)
00765         {
00766             gnc_error_dialog(NULL, "%s",
00767                              _("No Online Banking account found for this "
00768                                "gnucash account. These transactions will "
00769                                "not be executed by Online Banking."));
00770         }
00771     }
00772     else
00773     {
00774         data->ab_acc = NULL;
00775     }
00776 
00777     if (!data->generic_importer)
00778     {
00779         data->generic_importer = gnc_gen_trans_list_new(data->parent, NULL,
00780                                  TRUE, 14);
00781         if (data->execute_txns)
00782         {
00783             gnc_gen_trans_list_add_tp_cb(data->generic_importer,
00784                                          gnc_ab_trans_processed_cb, data);
00785         }
00786     }
00787 
00788     /* Iterate through all transactions */
00789     AB_ImExporterAccountInfo_TransactionsForEach(element, txn_transaction_cb,
00790             data);
00791 
00792     return NULL;
00793 }
00794 
00795 static AB_IMEXPORTER_ACCOUNTINFO *
00796 bal_accountinfo_cb(AB_IMEXPORTER_ACCOUNTINFO *element, gpointer user_data)
00797 {
00798     GncABImExContextImport *data = user_data;
00799     Account *gnc_acc;
00800     AB_ACCOUNT_STATUS *item, *best = NULL;
00801     const GWEN_TIME *best_time = NULL;
00802     const AB_BALANCE *booked_bal, *noted_bal;
00803     const AB_VALUE *booked_val = NULL, *noted_val = NULL;
00804     gdouble booked_value, noted_value;
00805     gnc_numeric value;
00806     time_t booked_tt = 0;
00807     GtkWidget *dialog;
00808     gboolean show_recn_window = FALSE;
00809 
00810     g_return_val_if_fail(element && data, NULL);
00811 
00812     if (data->awaiting & IGNORE_BALANCES)
00813         /* Ignore them */
00814         return NULL;
00815 
00816     if (!AB_ImExporterAccountInfo_GetFirstAccountStatus(element))
00817         /* No balance found */
00818         return NULL;
00819     else
00820         data->awaiting |= FOUND_BALANCES;
00821 
00822     /* Lookup the most recent ACCOUNT_STATUS available */
00823     item = AB_ImExporterAccountInfo_GetFirstAccountStatus(element);
00824     while (item)
00825     {
00826         const GWEN_TIME *item_time = AB_AccountStatus_GetTime(item);
00827         if (!best || GWEN_Time_Diff(best_time, item_time) < 0.0)
00828         {
00829             best = item;
00830             best_time = item_time;
00831         }
00832         item = AB_ImExporterAccountInfo_GetNextAccountStatus(element);
00833     }
00834 
00835     booked_bal = AB_AccountStatus_GetBookedBalance(best);
00836     if (!(data->awaiting & AWAIT_BALANCES))
00837     {
00838         /* Ignore zero balances if we don't await a balance */
00839         if (!booked_bal || AB_Value_IsZero(AB_Balance_GetValue(booked_bal)))
00840             return NULL;
00841 
00842         /* Ask the user whether to import unawaited non-zero balance */
00843         if (gnc_verify_dialog(data->parent, TRUE, "%s",
00844                               _("The bank has sent balance information "
00845                                 "in its response."
00846                                 "\n"
00847                                 "Do you want to import it?")))
00848         {
00849             data->awaiting |= AWAIT_BALANCES;
00850         }
00851         else
00852         {
00853             data->awaiting |= IGNORE_BALANCES;
00854             return NULL;
00855         }
00856     }
00857 
00858     /* Lookup the corresponding gnucash account */
00859     gnc_acc = gnc_ab_accinfo_to_gnc_acc(element);
00860     if (!gnc_acc) return NULL;
00861     data->gnc_acc = gnc_acc;
00862 
00863     /* Lookup booked balance and time */
00864     if (booked_bal)
00865     {
00866         const GWEN_TIME *ti = AB_Balance_GetTime(booked_bal);
00867         if (ti)
00868         {
00869             booked_tt =  GWEN_Time_toTime_t(ti);
00870         }
00871         else
00872         {
00873             /* No time found? Use today because the HBCI query asked for today's
00874              * balance. */
00875             booked_tt = gnc_timet_get_day_start(time(NULL));
00876         }
00877         booked_val = AB_Balance_GetValue(booked_bal);
00878         if (booked_val)
00879         {
00880             booked_value = AB_Value_GetValueAsDouble(booked_val);
00881         }
00882         else
00883         {
00884             g_warning("bal_accountinfo_cb: booked_val == NULL.  Assuming 0");
00885             booked_value = 0.0;
00886         }
00887     }
00888     else
00889     {
00890         g_warning("bal_accountinfo_cb: booked_bal == NULL.  Assuming 0");
00891         booked_tt = 0;
00892         booked_value = 0.0;
00893     }
00894 
00895     /* Lookup noted balance */
00896     noted_bal = AB_AccountStatus_GetNotedBalance(best);
00897     if (noted_bal)
00898     {
00899         noted_val = AB_Balance_GetValue(noted_bal);
00900         if (noted_val)
00901             noted_value = AB_Value_GetValueAsDouble(noted_val);
00902         else
00903         {
00904             g_warning("bal_accountinfo_cb: noted_val == NULL.  Assuming 0");
00905             noted_value = 0.0;
00906         }
00907     }
00908     else
00909     {
00910         g_warning("bal_accountinfo_cb: noted_bal == NULL.  Assuming 0");
00911         noted_value = 0.0;
00912     }
00913 
00914     value = double_to_gnc_numeric(booked_value,
00915                                   xaccAccountGetCommoditySCU(gnc_acc),
00916                                   GNC_HOW_RND_ROUND_HALF_UP);
00917     if (noted_value == 0.0 && booked_value == 0.0)
00918     {
00919         dialog = gtk_message_dialog_new(
00920                      GTK_WINDOW(data->parent),
00921                      GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
00922                      GTK_MESSAGE_INFO,
00923                      GTK_BUTTONS_OK,
00924                      "%s",
00925                      /* Translators: Strings from this file are needed only in
00926                       * countries that have one of aqbanking's Online Banking
00927                       * techniques available. This is 'OFX DirectConnect'
00928                       * (U.S. and others), 'HBCI' (in Germany), or 'YellowNet'
00929                       * (Switzerland). If none of these techniques are available
00930                       * in your country, you may safely ignore strings from the
00931                       * import-export/hbci subdirectory. */
00932                      _("The downloaded Online Banking Balance was zero.\n\n"
00933                        "Either this is the correct balance, or your bank does not "
00934                        "support Balance download in this Online Banking version. "
00935                        "In the latter case you should choose a different "
00936                        "Online Banking version number in the Online Banking "
00937                        "(AqBanking or HBCI) Setup. After that, try again to "
00938                        "download the Online Banking Balance."));
00939         gtk_dialog_run(GTK_DIALOG(dialog));
00940         gtk_widget_destroy(dialog);
00941 
00942     }
00943     else
00944     {
00945         gnc_numeric reconc_balance = xaccAccountGetReconciledBalance(gnc_acc);
00946 
00947         gchar *booked_str = gnc_AB_VALUE_to_readable_string(booked_val);
00948         gchar *message1 = g_strdup_printf(
00949                               _("Result of Online Banking job: \n"
00950                                 "Account booked balance is %s"),
00951                               booked_str);
00952         gchar *message2 =
00953             (noted_value == 0.0) ?
00954             g_strdup("") :
00955             g_strdup_printf(_("For your information: This account also "
00956                               "has a noted balance of %s\n"),
00957                             gnc_AB_VALUE_to_readable_string(noted_val));
00958 
00959         if (gnc_numeric_equal(value, reconc_balance))
00960         {
00961             const gchar *message3 =
00962                 _("The booked balance is identical to the current "
00963                   "reconciled balance of the account.");
00964             dialog = gtk_message_dialog_new(
00965                          GTK_WINDOW(data->parent),
00966                          GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
00967                          GTK_MESSAGE_INFO,
00968                          GTK_BUTTONS_OK,
00969                          "%s\n%s\n%s",
00970                          message1, message2, message3);
00971             gtk_dialog_run(GTK_DIALOG(dialog));
00972             gtk_widget_destroy(GTK_WIDGET(dialog));
00973 
00974         }
00975         else
00976         {
00977             const char *message3 = _("Reconcile account now?");
00978 
00979             show_recn_window = gnc_verify_dialog(data->parent, TRUE, "%s\n%s\n%s",
00980                                                  message1, message2, message3);
00981         }
00982         g_free(booked_str);
00983         g_free(message1);
00984         g_free(message2);
00985     }
00986 
00987     /* Show reconciliation window */
00988     if (show_recn_window)
00989         recnWindowWithBalance(data->parent, gnc_acc, value, booked_tt);
00990 
00991     return NULL;
00992 }
00993 
00994 GncABImExContextImport *
00995 gnc_ab_import_context(AB_IMEXPORTER_CONTEXT *context,
00996                       guint awaiting, gboolean execute_txns,
00997                       AB_BANKING *api, GtkWidget *parent)
00998 {
00999     GncABImExContextImport *data = g_new(GncABImExContextImport, 1);
01000 
01001     g_return_val_if_fail(context, NULL);
01002     /* Do not await and ignore at the same time */
01003     g_return_val_if_fail(!(awaiting & AWAIT_BALANCES)
01004                          || !(awaiting & IGNORE_BALANCES),
01005                          NULL);
01006     g_return_val_if_fail(!(awaiting & AWAIT_TRANSACTIONS)
01007                          || !(awaiting & IGNORE_TRANSACTIONS),
01008                          NULL);
01009     /* execute_txns must be FALSE if txns are not awaited */
01010     g_return_val_if_fail(awaiting & AWAIT_TRANSACTIONS || !execute_txns, NULL);
01011     /* An api is needed for the jobs */
01012     g_return_val_if_fail(!execute_txns || api, NULL);
01013 
01014     data->awaiting = awaiting;
01015     data->txn_found = FALSE;
01016     data->execute_txns = execute_txns;
01017     data->api = api;
01018     data->parent = parent;
01019     data->job_list = AB_Job_List2_new();
01020     data->tmp_job_list = NULL;
01021     data->generic_importer = NULL;
01022 
01023     g_datalist_init(&data->tmp_job_list);
01024 
01025     /* Import transactions */
01026     if (!(awaiting & IGNORE_TRANSACTIONS))
01027         AB_ImExporterContext_AccountInfoForEach(context, txn_accountinfo_cb,
01028                                                 data);
01029 
01030     /* Check balances */
01031     if (!(awaiting & IGNORE_BALANCES))
01032         AB_ImExporterContext_AccountInfoForEach(context, bal_accountinfo_cb,
01033                                                 data);
01034 
01035     return data;
01036 }
01037 
01038 guint
01039 gnc_ab_ieci_get_found(GncABImExContextImport *ieci)
01040 {
01041     g_return_val_if_fail(ieci, 0);
01042 
01043     return ieci->awaiting;
01044 }
01045 
01046 AB_JOB_LIST2 *
01047 gnc_ab_ieci_get_job_list(GncABImExContextImport *ieci)
01048 {
01049     g_return_val_if_fail(ieci, NULL);
01050 
01051     return ieci->job_list;
01052 }
01053 
01054 gboolean
01055 gnc_ab_ieci_run_matcher(GncABImExContextImport *ieci)
01056 {
01057     g_return_val_if_fail(ieci, FALSE);
01058 
01059     return gnc_gen_trans_list_run(ieci->generic_importer);
01060 }
01061 
01062 GWEN_DB_NODE *
01063 gnc_ab_get_permanent_certs(void)
01064 {
01065     int rv;
01066     GWEN_DB_NODE *perm_certs = NULL;
01067     AB_BANKING *banking = gnc_AB_BANKING_new();
01068 
01069     g_return_val_if_fail(banking, NULL);
01070 #ifdef AQBANKING_VERSION_4_PLUS
01071     rv = AB_Banking_LoadSharedConfig(banking, "certs", &perm_certs
01072 # ifndef AQBANKING_VERSION_5_PLUS
01073                                      , 0
01074 # endif
01075                                     );
01076 #else
01077     /* FIXME: Add code for older AqBanking versions */
01078     /* See QBankmanager 0.9.50 in src/kbanking/libs/kbanking.cpp lines 323ff
01079        for a proper example of how to do this */
01080     rv = 0;
01081 #endif
01082     gnc_AB_BANKING_fini(banking);
01083     g_return_val_if_fail(rv >= 0, NULL);
01084     return perm_certs;
01085 }
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines