|
GnuCash 2.4.99
|
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(¤t_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 }
1.7.4