GnuCash  5.6-150-g038405b370+
gnc-ofx-import.cpp
1 /*******************************************************************\
2  * This program is free software; you can redistribute it and/or *
3  * modify it under the terms of the GNU General Public License as *
4  * published by the Free Software Foundation; either version 2 of *
5  * the License, or (at your option) any later version. *
6  * *
7  * This program is distributed in the hope that it will be useful, *
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
10  * GNU General Public License for more details. *
11  * *
12  * You should have received a copy of the GNU General Public License*
13  * along with this program; if not, contact: *
14  * *
15  * Free Software Foundation Voice: +1-617-542-5942 *
16  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
17  * Boston, MA 02110-1301, USA gnu@gnu.org *
18 \********************************************************************/
26 #include <config.h>
27 
28 #include <gtk/gtk.h>
29 #include <glib/gi18n.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <sys/time.h>
33 #include <math.h>
34 #include <inttypes.h>
35 
36 #include <libofx/libofx.h>
37 #include "import-account-matcher.h"
39 #include "import-utilities.h"
40 #include "import-main-matcher.h"
41 
42 #include "Account.h"
43 #include "Transaction.h"
44 #include "engine-helpers.h"
45 #include "gnc-ofx-import.h"
46 #include "gnc-file.h"
47 #include "gnc-engine.h"
48 #include "gnc-ui-util.h"
49 #include "gnc-glib-utils.h"
50 #include "gnc-prefs.h"
51 #include "gnc-ui.h"
52 #include "gnc-window.h"
53 #include "dialog-account.h"
54 #include "dialog-utils.h"
55 #include "window-reconcile.h"
56 
57 #include <string>
58 #include <sstream>
59 #include <unordered_map>
60 
61 #define GNC_PREFS_GROUP "dialogs.import.ofx"
62 #define GNC_PREF_AUTO_COMMODITY "auto-create-commodity"
63 
64 static QofLogModule log_module = GNC_MOD_IMPORT;
65 
66 /********************************************************************\
67  * gnc_file_ofx_import
68  * Entry point
69 \********************************************************************/
70 
71 static gboolean auto_create_commodity = FALSE;
72 static Account *ofx_parent_account = NULL;
73 
74 typedef struct OfxTransactionData OfxTransactionData;
75 
76 // Structure we use to gather information about statement balance/account etc.
77 typedef struct _ofx_info
78 {
79  GtkWindow* parent;
80  GNCImportMainMatcher *gnc_ofx_importer_gui;
81  Account *last_import_account;
82  Account *last_investment_account;
83  Account *last_income_account;
84  gint num_trans_processed; // Number of transactions processed
85  GList* statement; // Statement, if any
86  gboolean run_reconcile; // If TRUE the reconcile window is opened after matching.
87  GSList* file_list; // List of OFX files to import
88  GList* trans_list; // We store the processed ofx transactions here
89  gint response; // Response sent by the match gui
90 } ofx_info ;
91 
92 static void runMatcher(ofx_info* info, char * selected_filename, gboolean go_to_next_file);
93 
94 /*
95 int ofx_proc_status_cb(struct OfxStatusData data)
96 {
97  return 0;
98 }
99 */
100 
101 static const char *PROP_OFX_INCOME_ACCOUNT = "ofx-income-account";
102 
103 static Account*
104 get_associated_income_account(const Account* investment_account)
105 {
106  GncGUID *income_guid = NULL;
107  Account *acct = NULL;
108  g_assert(investment_account);
109  qof_instance_get (QOF_INSTANCE (investment_account),
110  PROP_OFX_INCOME_ACCOUNT, &income_guid,
111  NULL);
112  acct = xaccAccountLookup (income_guid,
113  gnc_account_get_book(investment_account));
114  guid_free (income_guid);
115  return acct;
116 }
117 
118 static void
119 set_associated_income_account(Account* investment_account,
120  const Account *income_account)
121 {
122  const GncGUID * income_acc_guid;
123 
124  g_assert(investment_account);
125  g_assert(income_account);
126 
127  income_acc_guid = xaccAccountGetGUID(income_account);
128  xaccAccountBeginEdit(investment_account);
129  qof_instance_set (QOF_INSTANCE (investment_account),
130  PROP_OFX_INCOME_ACCOUNT, income_acc_guid,
131  NULL);
132  xaccAccountCommitEdit(investment_account);
133 }
134 
135 int ofx_proc_statement_cb (struct OfxStatementData data, void * statement_user_data);
136 int ofx_proc_security_cb (const struct OfxSecurityData data, void * security_user_data);
137 int ofx_proc_transaction_cb (OfxTransactionData data, void *user_data);
138 int ofx_proc_account_cb (struct OfxAccountData data, void * account_user_data);
139 static double ofx_get_investment_amount (const OfxTransactionData* data);
140 
141 static const gchar *gnc_ofx_ttype_to_string(TransactionType t)
142 {
143  switch (t)
144  {
145  case OFX_CREDIT:
146  return "Generic credit";
147  case OFX_DEBIT:
148  return "Generic debit";
149  case OFX_INT:
150  return "Interest earned or paid (Note: Depends on signage of amount)";
151  case OFX_DIV:
152  return "Dividend";
153  case OFX_FEE:
154  return "FI fee";
155  case OFX_SRVCHG:
156  return "Service charge";
157  case OFX_DEP:
158  return "Deposit";
159  case OFX_ATM:
160  return "ATM debit or credit (Note: Depends on signage of amount)";
161  case OFX_POS:
162  return "Point of sale debit or credit (Note: Depends on signage of amount)";
163  case OFX_XFER:
164  return "Transfer";
165  case OFX_CHECK:
166  return "Check";
167  case OFX_PAYMENT:
168  return "Electronic payment";
169  case OFX_CASH:
170  return "Cash withdrawal";
171  case OFX_DIRECTDEP:
172  return "Direct deposit";
173  case OFX_DIRECTDEBIT:
174  return "Merchant initiated debit";
175  case OFX_REPEATPMT:
176  return "Repeating payment/standing order";
177  case OFX_OTHER:
178  return "Other";
179  default:
180  return "Unknown transaction type";
181  }
182 }
183 
184 static const gchar *gnc_ofx_invttype_to_str(InvTransactionType t)
185 {
186  switch (t)
187  {
188  case OFX_BUYDEBT:
189  return "BUYDEBT (Buy debt security)";
190  case OFX_BUYMF:
191  return "BUYMF (Buy mutual fund)";
192  case OFX_BUYOPT:
193  return "BUYOPT (Buy option)";
194  case OFX_BUYOTHER:
195  return "BUYOTHER (Buy other security type)";
196  case OFX_BUYSTOCK:
197  return "BUYSTOCK (Buy stock))";
198  case OFX_CLOSUREOPT:
199  return "CLOSUREOPT (Close a position for an option)";
200  case OFX_INCOME:
201  return "INCOME (Investment income is realized as cash into the investment account)";
202  case OFX_INVEXPENSE:
203  return "INVEXPENSE (Misc investment expense that is associated with a specific security)";
204  case OFX_JRNLFUND:
205  return "JRNLFUND (Journaling cash holdings between subaccounts within the same investment account)";
206  case OFX_MARGININTEREST:
207  return "MARGININTEREST (Margin interest expense)";
208  case OFX_REINVEST:
209  return "REINVEST (Reinvestment of income)";
210  case OFX_RETOFCAP:
211  return "RETOFCAP (Return of capital)";
212  case OFX_SELLDEBT:
213  return "SELLDEBT (Sell debt security. Used when debt is sold, called, or reached maturity)";
214  case OFX_SELLMF:
215  return "SELLMF (Sell mutual fund)";
216  case OFX_SELLOPT:
217  return "SELLOPT (Sell option)";
218  case OFX_SELLOTHER:
219  return "SELLOTHER (Sell other type of security)";
220  case OFX_SELLSTOCK:
221  return "SELLSTOCK (Sell stock)";
222  case OFX_SPLIT:
223  return "SPLIT (Stock or mutial fund split)";
224  case OFX_TRANSFER:
225  return "TRANSFER (Transfer holdings in and out of the investment account)";
226 #ifdef HAVE_LIBOFX_VERSION_0_10
227  case OFX_INVBANKTRAN:
228  return "Transfer cash in and out of the investment account";
229 #endif
230  default:
231  return "ERROR, this investment transaction type is unknown. This is a bug in ofxdump";
232  }
233 
234 }
235 
236 static gchar*
237 sanitize_string (gchar* str)
238 {
239  gchar *inval;
240  const int length = -1; /*Assumes str is null-terminated */
241  while (!g_utf8_validate (str, length, (const gchar **)(&inval)))
242  *inval = '@';
243  return str;
244 }
245 
246 int ofx_proc_security_cb(const struct OfxSecurityData data, void * security_user_data)
247 {
248  char* cusip = NULL;
249  char* default_fullname = NULL;
250  char* default_mnemonic = NULL;
251 
252  if (data.unique_id_valid)
253  {
254  cusip = gnc_utf8_strip_invalid_strdup (data.unique_id);
255  }
256  if (data.secname_valid)
257  {
258  default_fullname = gnc_utf8_strip_invalid_strdup (data.secname);
259  }
260  if (data.ticker_valid)
261  {
262  default_mnemonic = gnc_utf8_strip_invalid_strdup (data.ticker);
263  }
264 
265  if (auto_create_commodity)
266  {
267  gnc_commodity *commodity =
269  FALSE,
270  default_fullname,
271  default_mnemonic);
272 
273  if (!commodity)
274  {
275  QofBook *book = gnc_get_current_book();
276  gnc_quote_source *source;
277  gint source_selection = 0; // FIXME: This is just a wild guess
278  char *commodity_namespace = NULL;
279  int fraction = 1;
280 
281  if (data.unique_id_type_valid)
282  {
283  commodity_namespace = gnc_utf8_strip_invalid_strdup (data.unique_id_type);
284  }
285 
286  g_warning("Creating a new commodity, cusip=%s", cusip);
287  /* Create the new commodity */
288  commodity = gnc_commodity_new(book,
289  default_fullname,
290  commodity_namespace,
291  default_mnemonic,
292  cusip,
293  fraction);
294 
295  /* Also set a single quote source */
296  gnc_commodity_begin_edit(commodity);
297  gnc_commodity_user_set_quote_flag (commodity, TRUE);
298  source = gnc_quote_source_lookup_by_ti (SOURCE_SINGLE, source_selection);
299  gnc_commodity_set_quote_source(commodity, source);
300  gnc_commodity_commit_edit(commodity);
301 
302  /* Remember the commodity */
303  gnc_commodity_table_insert(gnc_get_current_commodities(), commodity);
304 
305  g_free (commodity_namespace);
306 
307  }
308  }
309  else
310  {
312  TRUE,
313  default_fullname,
314  default_mnemonic);
315  }
316 
317  g_free (cusip);
318  g_free (default_mnemonic);
319  g_free (default_fullname);
320  return 0;
321 }
322 
323 static void gnc_ofx_set_split_memo(const OfxTransactionData* data, Split *split)
324 {
325  g_assert(data);
326  g_assert(split);
327  /* Also put the ofx transaction name in
328  * the splits memo field, or ofx memo if
329  * name is unavailable */
330  if (data->name_valid)
331  {
332  xaccSplitSetMemo(split, data->name);
333  }
334  else if (data->memo_valid)
335  {
336  xaccSplitSetMemo(split, data->memo);
337  }
338 }
339 static gnc_numeric gnc_ofx_numeric_from_double(double value, const gnc_commodity *commodity)
340 {
341  return double_to_gnc_numeric (value,
342  gnc_commodity_get_fraction(commodity),
344 }
345 static gnc_numeric gnc_ofx_numeric_from_double_txn(double value, const Transaction* txn)
346 {
347  return gnc_ofx_numeric_from_double(value, xaccTransGetCurrency(txn));
348 }
349 
350 /* Opens the dialog to create a new account with given name, commodity, parent, type.
351  * Returns the new account, or NULL if it couldn't be created.. */
352 static Account *gnc_ofx_new_account(GtkWindow* parent,
353  const char* name,
354  const gnc_commodity * account_commodity,
355  Account *parent_account,
356  GNCAccountType new_account_default_type)
357 {
358  Account *result;
359  GList * valid_types = NULL;
360 
361  g_assert(name);
362  g_assert(account_commodity);
363  g_assert(parent_account);
364 
365  if (new_account_default_type != ACCT_TYPE_NONE)
366  {
367  // Passing the types as gpointer
368  valid_types =
369  g_list_prepend(valid_types,
370  GINT_TO_POINTER(new_account_default_type));
371  if (!xaccAccountTypesCompatible(xaccAccountGetType(parent_account), new_account_default_type))
372  {
373  // Need to add the parent's account type
374  valid_types =
375  g_list_prepend(valid_types,
376  GINT_TO_POINTER(xaccAccountGetType(parent_account)));
377  }
378  }
379  result = gnc_ui_new_accounts_from_name_with_defaults (parent, name,
380  valid_types,
381  account_commodity,
382  parent_account);
383  g_list_free(valid_types);
384  return result;
385 }
386 /* LibOFX has a daylight time handling bug,
387  * https://sourceforge.net/p/libofx/bugs/39/, which causes it to adjust the
388  * timestamp for daylight time even when daylight time is not in
389  * effect. HAVE_OFX_BUG_39 reflects the result of checking for this bug during
390  * configuration, and fix_ofx_bug_39() corrects for it.
391  */
392 static time64
393 fix_ofx_bug_39 (time64 t)
394 {
395 #if HAVE_OFX_BUG_39
396  struct tm stm;
397 
398 #ifdef __FreeBSD__
399  time64 now;
400  /*
401  * FreeBSD has it's own libc implementation which differs from glibc. In particular:
402  * There is no daylight global
403  * tzname members are set to the string " " (three spaces) when not explicitly populated
404  *
405  * To check that the current timezone does not observe DST I check if tzname[1] starts with a space.
406  */
407  now = gnc_time (NULL);
408  gnc_localtime_r(&now, &stm);
409  tzset();
410 
411  if (tzname[1][0] != ' ' && !stm.tm_isdst)
412 #else
413  gnc_localtime_r(&t, &stm);
414  if (daylight && !stm.tm_isdst)
415 #endif
416  t += 3600;
417 #endif
418  return t;
419 }
420 
421 static void
422 set_transaction_dates(Transaction *transaction, OfxTransactionData *data)
423 {
424  /* Note: Unfortunately libofx <= 0.9.5 will not report a missing
425  * date field as an invalid one. Instead, it will report it as
426  * valid and return a completely bogus date. Starting with
427  * libofx-0.9.6 (not yet released as of 2012-09-09), it will still
428  * be reported as valid but at least the date integer itself is
429  * just plain zero. */
430 
431  time64 current_time = gnc_time (NULL);
432 
433  if (data->date_posted_valid && (data->date_posted != 0))
434  {
435  /* The hopeful case: We have a posted_date */
436  data->date_posted = fix_ofx_bug_39 (data->date_posted);
437  xaccTransSetDatePostedSecsNormalized(transaction, data->date_posted);
438  }
439  else if (data->date_initiated_valid && (data->date_initiated != 0))
440  {
441  /* No posted date? Maybe we have an initiated_date */
442  data->date_initiated = fix_ofx_bug_39 (data->date_initiated);
443  xaccTransSetDatePostedSecsNormalized(transaction, data->date_initiated);
444  }
445  else
446  {
447  /* Uh no, no valid date. As a workaround use today's date */
448  xaccTransSetDatePostedSecsNormalized(transaction, current_time);
449  }
450 
451  xaccTransSetDateEnteredSecs(transaction, current_time);
452 }
453 
454 static void
455 fill_transaction_description(Transaction *transaction, OfxTransactionData *data)
456 {
457  /* Put transaction name in Description, or memo if name unavailable */
458  if (data->name_valid)
459  {
460  xaccTransSetDescription(transaction, data->name);
461  }
462  else if (data->memo_valid)
463  {
464  xaccTransSetDescription(transaction, data->memo);
465  }
466 }
467 
468 static void
469 fill_transaction_notes(Transaction *transaction, OfxTransactionData *data)
470 {
471  /* Put everything else in the Notes field */
472  char *notes = g_strdup_printf("OFX ext. info: ");
473 
474  if (data->transactiontype_valid)
475  {
476  char *tmp = notes;
477  notes = g_strdup_printf("%s%s%s", tmp, "|Trans type:",
478  gnc_ofx_ttype_to_string(data->transactiontype));
479  g_free(tmp);
480  }
481 
482  if (data->invtransactiontype_valid)
483  {
484  char *tmp = notes;
485  notes = g_strdup_printf("%s%s%s", tmp, "|Investment Trans type:",
486  gnc_ofx_invttype_to_str(data->invtransactiontype));
487  g_free(tmp);
488  }
489  if (data->memo_valid && data->name_valid) /* Copy only if memo wasn't put in Description */
490  {
491  char *tmp = notes;
492  notes = g_strdup_printf("%s%s%s", tmp, "|Memo:", data->memo);
493  g_free(tmp);
494  }
495  if (data->date_funds_available_valid)
496  {
497  char dest_string[MAX_DATE_LENGTH];
498  time64 time = data->date_funds_available;
499  char *tmp = notes;
500 
501  gnc_time64_to_iso8601_buff (time, dest_string);
502  notes = g_strdup_printf("%s%s%s", tmp,
503  "|Date funds available:", dest_string);
504  g_free(tmp);
505  }
506  if (data->server_transaction_id_valid)
507  {
508  char *tmp = notes;
509  notes = g_strdup_printf("%s%s%s", tmp,
510  "|Server trans ID (conf. number):",
511  sanitize_string (data->server_transaction_id));
512  g_free(tmp);
513  }
514  if (data->standard_industrial_code_valid)
515  {
516  char *tmp = notes;
517  notes = g_strdup_printf("%s%s%ld", tmp,
518  "|Standard Industrial Code:",
519  data->standard_industrial_code);
520  g_free(tmp);
521 
522  }
523  if (data->payee_id_valid)
524  {
525  char *tmp = notes;
526  notes = g_strdup_printf("%s%s%s", tmp, "|Payee ID:",
527  sanitize_string (data->payee_id));
528  g_free(tmp);
529  }
530  //PERR("WRITEME: GnuCash ofx_proc_transaction():Add PAYEE and ADDRESS here once supported by libofx! Notes=%s\n", notes);
531 
532  /* Ideally, gnucash should process the corrected transactions */
533  if (data->fi_id_corrected_valid)
534  {
535  char *tmp = notes;
536  PERR("WRITEME: GnuCash ofx_proc_transaction(): WARNING: This transaction corrected a previous transaction, but we created a new one instead!\n");
537  notes = g_strdup_printf("%s%s%s%s", tmp,
538  "|This corrects transaction #",
539  sanitize_string (data->fi_id_corrected),
540  "but GnuCash didn't process the correction!");
541  g_free(tmp);
542  }
543  xaccTransSetNotes(transaction, notes);
544  g_free(notes);
545 
546 }
547 
548 static void
549 process_bank_transaction(Transaction *transaction, Account *import_account,
550  OfxTransactionData *data, ofx_info *info)
551 {
552  Split *split;
553  gnc_numeric gnc_amount;
554  QofBook *book = qof_instance_get_book(QOF_INSTANCE(transaction));
555  double amount = data->amount;
556 #ifdef HAVE_LIBOFX_VERSION_0_10
557  if (data->currency_ratio_valid && data->currency_ratio != 0)
558  amount *= data->currency_ratio;
559 #endif
560  /***** Process a normal transaction ******/
561  DEBUG("Adding split; Ordinary banking transaction, money flows from or into the source account");
562  split = xaccMallocSplit(book);
563  xaccTransAppendSplit(transaction, split);
564  xaccAccountInsertSplit(import_account, split);
565  gnc_amount = gnc_ofx_numeric_from_double_txn(amount, transaction);
566  xaccSplitSetBaseValue(split, gnc_amount, xaccTransGetCurrency(transaction));
567 
568  /* set tran-num and/or split-action per book option */
569  if (data->check_number_valid)
570  {
571  /* SQL will correctly interpret the string "null", but
572  * the transaction num field is declared to be
573  * non-null so substitute the empty string.
574  */
575  const char *num_value =
576  strcasecmp (data->check_number, "null") == 0 ? "" :
577  data->check_number;
578  gnc_set_num_action(transaction, split, num_value, NULL);
579  }
580  else if (data->reference_number_valid)
581  {
582  const char *num_value =
583  strcasecmp (data->reference_number, "null") == 0 ? "" :
584  data->check_number;
585  gnc_set_num_action(transaction, split, num_value, NULL);
586  }
587  /* Also put the ofx transaction's memo in the
588  * split's memo field */
589  if (data->memo_valid)
590  {
591  xaccSplitSetMemo(split, data->memo);
592  }
593  if (data->fi_id_valid)
594  {
595  xaccSplitSetOnlineID(split, sanitize_string (data->fi_id));
596  }
597 }
598 
599 typedef struct
600 {
601  gnc_commodity *commodity;
602  char *online_id;
603  char *acct_text;
604  gboolean choosing;
606 
607 static Account*
608 create_investment_subaccount(GtkWindow *parent, Account* parent_acct,
609  InvestmentAcctData *inv_data)
610 {
611 
612  Account *investment_account =
613  gnc_ofx_new_account(parent,
614  inv_data->acct_text,
615  inv_data->commodity,
616  parent_acct,
618  if (investment_account)
619  {
620  xaccAccountSetOnlineID(investment_account, inv_data->online_id);
621  inv_data->choosing = FALSE;
622  ofx_parent_account = parent_acct;
623  }
624  else
625  {
626  ofx_parent_account = NULL;
627  }
628  return investment_account;
629 }
630 
631 static gboolean
632 continue_account_selection(GtkWidget* parent, Account* account,
633  gnc_commodity* commodity)
634 {
635  gboolean keep_going =
636  gnc_verify_dialog(
637  GTK_WINDOW (parent), TRUE,
638  "The chosen account \"%s\" does not have the correct "
639  "currency/security \"%s\" (it has \"%s\" instead). "
640  "This account cannot be used. "
641  "Do you want to choose again?",
642  xaccAccountGetName(account),
643  gnc_commodity_get_fullname(commodity),
645  // We must also delete the online_id that was set in gnc_import_select_account()
646  xaccAccountSetOnlineID(account, "");
647  return keep_going;
648 }
649 
650 static Account*
651 choose_investment_account_helper(OfxTransactionData *data, ofx_info *info,
652  InvestmentAcctData *inv_data)
653 {
654  Account *investment_account, *parent_account;
655 
656  if (xaccAccountGetCommodity(info->last_investment_account) == inv_data->commodity)
657  parent_account = info->last_investment_account;
658  else
659  parent_account = ofx_parent_account;
660 
661  investment_account =
662  gnc_import_select_account(GTK_WIDGET(info->parent),
663  inv_data->online_id,
664  TRUE, inv_data->acct_text,
665  inv_data->commodity, ACCT_TYPE_STOCK,
666  parent_account, &inv_data->choosing);
667  if (investment_account &&
668  xaccAccountGetCommodity(investment_account) == inv_data->commodity)
669  {
670  Account *parent_account = gnc_account_get_parent(investment_account);
671 
672  if (!ofx_parent_account && parent_account &&
673  !gnc_account_is_root(parent_account) &&
676  ofx_parent_account = parent_account;
677 
678  info->last_investment_account = investment_account;
679  return investment_account;
680  }
681 
682  /* That didn't work out. Create a subaccount if we can. */
683  if (auto_create_commodity && ofx_parent_account)
684  {
685  investment_account =
686  create_investment_subaccount(GTK_WINDOW(info->parent),
687  ofx_parent_account,
688  inv_data);
689  }
690  else
691  {
692  // No account with matching commodity. Ask the user
693  // whether to continue or abort.
694  inv_data->choosing =
695  continue_account_selection(GTK_WIDGET(info->parent),
696  investment_account, inv_data->commodity);
697  investment_account = NULL;
698  }
699 
700  return investment_account;
701 }
702 
703 static Account*
704 choose_investment_account(OfxTransactionData *data, ofx_info *info,
705  gnc_commodity *commodity)
706 {
707  Account* investment_account = NULL;
708  InvestmentAcctData inv_data = {commodity, NULL, NULL, TRUE};
709 
710  // As we now have the commodity, select the account with that commodity.
711 
712  /* Translators: This string is a default account name. It MUST
713  * NOT contain the character ':' anywhere in it or in any
714  * translations. */
715  inv_data.acct_text = g_strdup_printf(
716  _("Stock account for security \"%s\""),
717  sanitize_string (data->security_data_ptr->secname));
718 
719  inv_data.online_id =
720  g_strdup_printf("%s%s", data->account_id, data->unique_id);
721 
722  // Loop until we either have an account, or the user pressed Cancel
723  while (!investment_account && inv_data.choosing)
724  investment_account = choose_investment_account_helper(data, info,
725  &inv_data);
726  if (!investment_account)
727  {
728  PERR("No investment account found for text: %s\n", inv_data.acct_text);
729  }
730  g_free (inv_data.acct_text);
731  g_free (inv_data.online_id);
732 
733  return investment_account;
734 }
735 
736 static Account*
737 choose_income_account(Account* investment_account, Transaction *transaction,
738  OfxTransactionData *data, ofx_info *info)
739 {
740  Account *income_account = NULL;
741  DEBUG("Now let's find an account for the destination split");
742  income_account =
743  get_associated_income_account(investment_account);
744 
745  if (income_account == NULL)
746  {
747  char *income_account_text;
748  gnc_commodity *currency = xaccTransGetCurrency(transaction);
749  DEBUG("Couldn't find an associated income account");
750  /* Translators: This string is a default account
751  * name. It MUST NOT contain the character ':' anywhere
752  * in it or in any translations. */
753  income_account_text = g_strdup_printf(
754  _("Income account for security \"%s\""),
755  sanitize_string (data->security_data_ptr->secname));
756  income_account =
757  gnc_import_select_account(GTK_WIDGET(info->parent), NULL, TRUE,
758  income_account_text, currency,
760  info->last_income_account, NULL);
761 
762  if (income_account != NULL)
763  {
764  info->last_income_account = income_account;
765  set_associated_income_account(investment_account,
766  income_account);
767  DEBUG("KVP written");
768  }
769  }
770  else
771  {
772  DEBUG("Found at least one associated income account");
773  }
774 
775  return income_account;
776 }
777 
778 static void
779 add_investment_split(Transaction* transaction, Account* account,
780  OfxTransactionData *data)
781 {
782  Split *split;
783  QofBook *book = gnc_account_get_book(account);
784  gnc_numeric gnc_amount, gnc_units;
785  gnc_commodity *commodity = xaccAccountGetCommodity(account);
786  DEBUG("Adding investment split; Money flows from or into the stock account");
787  split = xaccMallocSplit(book);
788  xaccTransAppendSplit(transaction, split);
789  xaccAccountInsertSplit(account, split);
790 
791  gnc_amount =
792  gnc_ofx_numeric_from_double_txn(ofx_get_investment_amount(data),
793  transaction);
794  gnc_units = gnc_ofx_numeric_from_double (data->units, commodity);
795  xaccSplitSetAmount(split, gnc_units);
796  xaccSplitSetValue(split, gnc_amount);
797 
798  /* set tran-num and/or split-action per book option */
799  if (data->check_number_valid)
800  {
801  gnc_set_num_action(transaction, split, data->check_number, NULL);
802  }
803  else if (data->reference_number_valid)
804  {
805  gnc_set_num_action(transaction, split,
806  data->reference_number, NULL);
807  }
808  if (data->security_data_ptr->memo_valid)
809  {
810  xaccSplitSetMemo(split,
811  sanitize_string (data->security_data_ptr->memo));
812  }
813  if (data->fi_id_valid &&
816  {
817  xaccSplitSetOnlineID(split, sanitize_string (data->fi_id));
818  }
819 }
820 
821 static void
822 add_currency_split(Transaction *transaction, Account* account,
823  double amount, OfxTransactionData *data)
824 {
825  Split *split;
826  QofBook *book = gnc_account_get_book(account);
827  gnc_numeric gnc_amount;
828 
829  split = xaccMallocSplit(book);
830  xaccTransAppendSplit(transaction, split);
831  xaccAccountInsertSplit(account, split);
832  gnc_amount = gnc_ofx_numeric_from_double_txn(amount, transaction);
833  xaccSplitSetBaseValue(split, gnc_amount, xaccTransGetCurrency(transaction));
834 
835  // Set split memo from ofx transaction name or memo
836  gnc_ofx_set_split_memo(data, split);
837  if (data->fi_id_valid)
838  xaccSplitSetOnlineID (split, sanitize_string (data->fi_id));
839 }
840 
841 /* ******** Process an investment transaction **********/
842 /* Note that the ACCT_TYPE_STOCK account type
843  should be replaced with something derived from
844  data->invtranstype*/
845 
846 static void
847 process_investment_transaction(Transaction *transaction, Account *import_account,
848  OfxTransactionData *data, ofx_info *info)
849 {
850  Account *investment_account = NULL;
851  Account *income_account = NULL;
852  gnc_commodity *investment_commodity;
853  double amount = data->amount;
854 
855  g_return_if_fail(data->invtransactiontype_valid);
856 
857  gnc_utf8_strip_invalid (data->unique_id);
858 
859 
860  // Set the cash split unless it's a reinvestment, which doesn't have one.
861  if (data->invtransactiontype != OFX_REINVEST)
862  {
863  DEBUG("Adding investment cash split.");
864  add_currency_split(transaction, import_account,
865  -ofx_get_investment_amount(data), data);
866  }
867 
868  investment_commodity = gnc_import_select_commodity(data->unique_id,
869  FALSE, NULL, NULL);
870  if (!investment_commodity)
871  {
872  PERR("Commodity not found for the investment transaction");
873  return;
874  }
875  investment_account = choose_investment_account(data, info,
876  investment_commodity);
877 
878  if (!investment_account)
879  {
880  PERR("Failed to determine an investment asset account.");
881  return;
882  }
883 
884  if (data->invtransactiontype != OFX_INCOME)
885  {
886  if (data->unitprice_valid && data->units_valid)
887  add_investment_split(transaction, investment_account, data);
888  else
889  PERR("Unable to add investment split, unit price or units were invalid.");
890  }
891 
892  if (!(data->invtransactiontype == OFX_REINVEST
893  || data->invtransactiontype == OFX_INCOME))
894  //Done
895  return;
896 
897 #ifdef HAVE_LIBOFX_VERSION_0_10
898  if (data->currency_ratio_valid && data->currency_ratio != 0)
899  amount *= data->currency_ratio;
900 #endif
901  income_account = choose_income_account(investment_account,
902  transaction, data, info);
903  g_return_if_fail(income_account);
904 
905  DEBUG("Adding investment income split.");
906  if (data->invtransactiontype == OFX_REINVEST)
907  add_currency_split(transaction, income_account, amount, data);
908  else
909  add_currency_split(transaction, income_account, -amount, data);
910 }
911 
912 int ofx_proc_transaction_cb(OfxTransactionData data, void *user_data)
913 {
914  Account *import_account;
915  gnc_commodity *currency = NULL;
916  QofBook *book;
917  Transaction *transaction;
918  ofx_info* info = (ofx_info*) user_data;
919 
920  g_assert(info->parent);
921 
922  if (!data.amount_valid)
923  {
924  PERR("The transaction doesn't have a valid amount");
925  return 0;
926  }
927 
928  if (!data.account_id_valid)
929  {
930  PERR("account ID for this transaction is unavailable!");
931  return 0;
932  }
933 
934  gnc_utf8_strip_invalid (data.account_id);
935 
936  import_account = gnc_import_select_account(GTK_WIDGET(info->parent),
937  data.account_id,
938  0, NULL, NULL, ACCT_TYPE_NONE,
939  info->last_import_account, NULL);
940  if (import_account == NULL)
941  {
942  PERR("Unable to find account for id %s", data.account_id);
943  return 0;
944  }
945  info->last_import_account = import_account;
946  /***** Validate the input strings to ensure utf8 *****/
947  if (data.name_valid)
948  gnc_utf8_strip_invalid(data.name);
949  if (data.memo_valid)
950  gnc_utf8_strip_invalid(data.memo);
951  if (data.check_number_valid)
952  gnc_utf8_strip_invalid(data.check_number);
953  if (data.reference_number_valid)
954  gnc_utf8_strip_invalid(data.reference_number);
955 
956  /***** Create the transaction and setup transaction data *******/
957  book = gnc_account_get_book(import_account);
958  transaction = xaccMallocTransaction(book);
959  xaccTransBeginEdit(transaction);
960 
961  set_transaction_dates(transaction, &data);
962  fill_transaction_description(transaction, &data);
963  fill_transaction_notes(transaction, &data);
964 
965  if (data.account_ptr && data.account_ptr->currency_valid)
966  {
967  DEBUG("Currency from libofx: %s", data.account_ptr->currency);
968  currency = gnc_commodity_table_lookup( gnc_get_current_commodities (),
969  GNC_COMMODITY_NS_CURRENCY,
970  data.account_ptr->currency);
971  }
972  else
973  {
974  DEBUG("Currency from libofx unavailable, defaulting to account's default");
975  currency = xaccAccountGetCommodity(import_account);
976  }
977 
978  xaccTransSetCurrency(transaction, currency);
979 
980  if (!data.invtransactiontype_valid
981 #ifdef HAVE_LIBOFX_VERSION_0_10
982  || data.invtransactiontype == OFX_INVBANKTRAN
983 #endif
984  )
985  process_bank_transaction(transaction, import_account, &data, info);
986  else if (data.unique_id_valid
987  && data.security_data_valid
988  && data.security_data_ptr != NULL
989  && data.security_data_ptr->secname_valid)
990  process_investment_transaction(transaction, import_account,
991  &data, info);
992  else
993  {
994  PERR("Unsupported OFX transaction type.");
995  xaccTransDestroy(transaction);
996  xaccTransCommitEdit(transaction);
997  return 0;
998  }
999 
1000  /* Send transaction to importer GUI. */
1001  if (xaccTransCountSplits(transaction) > 0)
1002  {
1003  DEBUG("%d splits sent to the importer gui",
1004  xaccTransCountSplits(transaction));
1005  info->trans_list = g_list_prepend (info->trans_list, transaction);
1006  }
1007  else
1008  {
1009  PERR("No splits in transaction (missing account?), ignoring.");
1010  xaccTransDestroy(transaction);
1011  xaccTransCommitEdit(transaction);
1012  }
1013 
1014  info->num_trans_processed += 1;
1015  return 0;
1016 }//end ofx_proc_transaction()
1017 
1018 
1019 int ofx_proc_statement_cb (struct OfxStatementData data, void * statement_user_data)
1020 {
1021  ofx_info* info = (ofx_info*) statement_user_data;
1022  struct OfxStatementData *statement = g_new (struct OfxStatementData, 1);
1023  *statement = data;
1024  info->statement = g_list_prepend (info->statement, statement);
1025  return 0;
1026 }
1027 
1028 
1029 int ofx_proc_account_cb(struct OfxAccountData data, void * account_user_data)
1030 {
1031  gnc_commodity_table * commodity_table;
1032  gnc_commodity * default_commodity;
1033  GNCAccountType default_type = ACCT_TYPE_NONE;
1034  gchar * account_description;
1035  GtkWidget * main_widget;
1036  GtkWidget * parent;
1037  /* In order to trigger a book options display on the creation of a new book,
1038  * we need to detect when we are dealing with a new book. */
1039  gboolean new_book = gnc_is_new_book();
1040  ofx_info* info = (ofx_info*) account_user_data;
1041  Account* account = NULL;
1042 
1043  const gchar * account_type_name = _("Unknown OFX account");
1044 
1045  if (data.account_id_valid)
1046  {
1047  commodity_table = gnc_get_current_commodities ();
1048  if (data.currency_valid)
1049  {
1050  DEBUG("Currency from libofx: %s", data.currency);
1051  default_commodity = gnc_commodity_table_lookup(commodity_table,
1052  GNC_COMMODITY_NS_CURRENCY,
1053  data.currency);
1054  }
1055  else
1056  {
1057  default_commodity = NULL;
1058  }
1059 
1060  if (data.account_type_valid)
1061  {
1062  switch (data.account_type)
1063  {
1064  case OfxAccountData::OFX_CHECKING:
1065  default_type = ACCT_TYPE_BANK;
1066  account_type_name = _("Unknown OFX checking account");
1067  break;
1068  case OfxAccountData::OFX_SAVINGS:
1069  default_type = ACCT_TYPE_BANK;
1070  account_type_name = _("Unknown OFX savings account");
1071  break;
1072  case OfxAccountData::OFX_MONEYMRKT:
1073  default_type = ACCT_TYPE_MONEYMRKT;
1074  account_type_name = _("Unknown OFX money market account");
1075  break;
1076  case OfxAccountData::OFX_CREDITLINE:
1077  default_type = ACCT_TYPE_CREDITLINE;
1078  account_type_name = _("Unknown OFX credit line account");
1079  break;
1080  case OfxAccountData::OFX_CMA:
1081  default_type = ACCT_TYPE_NONE;
1082  /* Cash Management Account */
1083  account_type_name = _("Unknown OFX CMA account");
1084  break;
1085  case OfxAccountData::OFX_CREDITCARD:
1086  default_type = ACCT_TYPE_CREDIT;
1087  account_type_name = _("Unknown OFX credit card account");
1088  break;
1089  case OfxAccountData::OFX_INVESTMENT:
1090  default_type = ACCT_TYPE_BANK;
1091  account_type_name = _("Unknown OFX investment account");
1092  break;
1093  default:
1094  PERR("WRITEME: ofx_proc_account() This is an unknown account type!");
1095  break;
1096  }
1097  }
1098 
1099  /* If the OFX importer was started in Gnucash in a 'new_book' situation,
1100  * as described above, the first time the 'ofx_proc_account_cb' function
1101  * is called a book is created. (This happens after the 'new_book' flag
1102  * is set in 'gnc_get_current_commodities', called above.) So, before
1103  * calling 'gnc_import_select_account', allow the user to set book
1104  * options. */
1105  if (new_book)
1106  gnc_new_book_option_display (GTK_WIDGET (gnc_ui_get_main_window (NULL)));
1107 
1108  gnc_utf8_strip_invalid(data.account_name);
1109  gnc_utf8_strip_invalid(data.account_id);
1110  account_description = g_strdup_printf (/* This string is a default account
1111  name. It MUST NOT contain the
1112  character ':' anywhere in it or
1113  in any translation. */
1114  "%s \"%s\"",
1115  account_type_name,
1116  data.account_name);
1117 
1118  main_widget = gnc_gen_trans_list_widget (info->gnc_ofx_importer_gui);
1119 
1120  /* On first use, the import-main-matcher is hidden / not realized so to
1121  * get a parent use the transient parent of the matcher */
1122  if (gtk_widget_get_realized (main_widget))
1123  parent = main_widget;
1124  else
1125  parent = GTK_WIDGET(gtk_window_get_transient_for (GTK_WINDOW(main_widget)));
1126 
1127  account = gnc_import_select_account (parent,
1128  data.account_id, 1,
1129  account_description, default_commodity,
1130  default_type, NULL, NULL);
1131 
1132  if (account)
1133  {
1134  info->last_import_account = account;
1135  }
1136 
1137  g_free(account_description);
1138  }
1139  else
1140  {
1141  PERR("account online ID not available");
1142  }
1143 
1144  return 0;
1145 }
1146 
1147 double ofx_get_investment_amount(const OfxTransactionData* data)
1148 {
1149  double amount = data->amount;
1150 #ifdef HAVE_LIBOFX_VERSION_0_10
1151  if (data->invtransactiontype == OFX_INVBANKTRAN)
1152  return 0.0;
1153  if (data->currency_ratio_valid && data->currency_ratio != 0)
1154  amount *= data->currency_ratio;
1155 #endif
1156  g_assert(data);
1157  switch (data->invtransactiontype)
1158  {
1159  case OFX_BUYDEBT:
1160  case OFX_BUYMF:
1161  case OFX_BUYOPT:
1162  case OFX_BUYOTHER:
1163  case OFX_BUYSTOCK:
1164  return fabs(amount);
1165  case OFX_SELLDEBT:
1166  case OFX_SELLMF:
1167  case OFX_SELLOPT:
1168  case OFX_SELLOTHER:
1169  case OFX_SELLSTOCK:
1170  return -1 * fabs(amount);
1171  default:
1172  return -1 * amount;
1173  }
1174 }
1175 
1176 // Forward declaration, required because several static functions depend on one-another.
1177 static void
1178 gnc_file_ofx_import_process_file (ofx_info* info);
1179 
1180 // gnc_ofx_process_next_file processes the next file in the info->file_list.
1181 static void
1182 gnc_ofx_process_next_file (GtkDialog *dialog, gpointer user_data)
1183 {
1184  ofx_info* info = (ofx_info*) user_data;
1185  // Free the statement (if it was allocated)
1186  g_list_free_full (info->statement, g_free);
1187  info->statement = NULL;
1188 
1189  // Done with the previous OFX file, process the next one if any.
1190  info->file_list = g_slist_delete_link (info->file_list, info->file_list);
1191  if (info->file_list)
1192  gnc_file_ofx_import_process_file (info);
1193  else
1194  {
1195  // Final cleanup.
1196  g_free (info);
1197  }
1198 }
1199 
1200 static void
1201 gnc_ofx_on_match_click (GtkDialog *dialog, gint response_id, gpointer user_data)
1202 {
1203  // Record the response of the user. If cancel we won't go to the next file, etc.
1204  ofx_info* info = (ofx_info*)user_data;
1205  info->response = response_id;
1206 }
1207 
1208 static void
1209 gnc_ofx_match_done (GtkDialog *dialog, gpointer user_data)
1210 {
1211  ofx_info* info = (ofx_info*) user_data;
1212 
1213  /* The the user did not click OK, don't process the rest of the
1214  * transaction, don't go to the next of xfile.
1215  */
1216  if (info->response != GTK_RESPONSE_OK)
1217  return;
1218 
1219  if (info->trans_list)
1220  {
1221  /* Re-run the match dialog if there are transactions
1222  * remaining in our list (happens if several accounts exist
1223  * in the same ofx).
1224  */
1225  info->gnc_ofx_importer_gui = gnc_gen_trans_list_new (GTK_WIDGET (info->parent), NULL, FALSE, 42, FALSE);
1226  runMatcher (info, NULL, true);
1227  return;
1228  }
1229 
1230  if (info->run_reconcile && info->statement && info->statement->data)
1231  {
1232  auto statement = static_cast<struct OfxStatementData*>(info->statement->data);
1233  // Open a reconcile window.
1234  Account* account = gnc_import_select_account (gnc_gen_trans_list_widget(info->gnc_ofx_importer_gui),
1235  statement->account_id,
1236  0, NULL, NULL, ACCT_TYPE_NONE, NULL, NULL);
1237  if (account && statement->ledger_balance_valid)
1238  {
1239  gnc_numeric value = double_to_gnc_numeric (statement->ledger_balance,
1240  xaccAccountGetCommoditySCU (account),
1242 
1243  RecnWindow* rec_window = recnWindowWithBalance (GTK_WIDGET (info->parent), account, value,
1244  statement->ledger_balance_date);
1245 
1246  // Connect to destroy, at which point we'll process the next OFX file..
1247  g_signal_connect (G_OBJECT (gnc_ui_reconcile_window_get_window (rec_window)), "destroy",
1248  G_CALLBACK (gnc_ofx_match_done), info);
1249  if (info->statement->next)
1250  info->statement = info->statement->next;
1251  else
1252  {
1253  g_list_free_full (g_list_first (info->statement), g_free);
1254  info->statement = NULL;
1255  }
1256  return;
1257  }
1258  }
1259  else
1260  {
1261  if (info->statement && info->statement->next)
1262  {
1263  info->statement = info->statement->next;
1264  gnc_ofx_match_done (dialog, user_data);
1265  return;
1266  }
1267  else
1268  {
1269  g_list_free_full (g_list_first (info->statement), g_free);
1270  info->statement = NULL;
1271  }
1272  }
1273  gnc_ofx_process_next_file (NULL, info);
1274 }
1275 
1276 // This callback is triggered when the user checks or unchecks the reconcile after match
1277 // check box in the matching dialog.
1278 static void
1279 reconcile_when_close_toggled_cb (GtkToggleButton *togglebutton, ofx_info* info)
1280 {
1281  info->run_reconcile = gtk_toggle_button_get_active (togglebutton);
1282 }
1283 
1284 static std::string
1285 make_date_amount_key (const Split* split)
1286 {
1287  std::ostringstream ss;
1288  auto _amount = gnc_numeric_reduce (gnc_numeric_abs (xaccSplitGetAmount (split)));
1289  ss << _amount.num << '/' << _amount.denom << ' ' << xaccTransGetDate (xaccSplitGetParent (split));
1290  return ss.str();
1291 }
1292 
1293 static void
1294 runMatcher (ofx_info* info, char * selected_filename, gboolean go_to_next_file)
1295 {
1296  GtkWindow *parent = info->parent;
1297  GList* trans_list_remain = NULL;
1298  std::unordered_map <std::string,Account*> trans_map;
1299 
1300  /* If we have multiple accounts in the ofx file, we need to
1301  * avoid processing transfers between accounts together because this will
1302  * create duplicate entries.
1303  */
1304  info->num_trans_processed = 0;
1305 
1306  gnc_window_show_progress (_("Removing duplicate transactions…"), 100);
1307 
1308  // Add transactions, but verify that there isn't one that was
1309  // already added with identical amounts and date, and a different
1310  // account. To do that, create a hash table whose key is a hash of
1311  // amount and date, and whose value is the account in which they
1312  // appear.
1313  for(GList* node = info->trans_list; node; node=node->next)
1314  {
1315  auto trans = static_cast<Transaction*>(node->data);
1316  Split* split = xaccTransGetSplit (trans, 0);
1317  Account* account = xaccSplitGetAccount (split);
1318  auto date_amount_key = make_date_amount_key (split);
1319 
1320  auto it = trans_map.find (date_amount_key);
1321  if (it != trans_map.end() && it->second != account)
1322  {
1323  if (qof_log_check (G_LOG_DOMAIN, QOF_LOG_DEBUG))
1324  {
1325  // There is a transaction with identical amounts and
1326  // dates, but a different account. That's a potential
1327  // transfer so process this transaction in a later call.
1328  gchar *name1 = gnc_account_get_full_name (account);
1329  gchar *name2 = gnc_account_get_full_name (it->second);
1330  gchar *amtstr = gnc_numeric_to_string (xaccSplitGetAmount (split));
1331  gchar *datestr = qof_print_date (xaccTransGetDate (trans));
1332  DEBUG ("Potential transfer %s %s %s %s\n", name1, name2, amtstr, datestr);
1333  g_free (name1);
1334  g_free (name2);
1335  g_free (amtstr);
1336  g_free (datestr);
1337  }
1338  trans_list_remain = g_list_prepend (trans_list_remain, trans);
1339  }
1340  else
1341  {
1342  trans_map[date_amount_key] = account;
1343  gnc_gen_trans_list_add_trans (info->gnc_ofx_importer_gui, trans);
1344  info->num_trans_processed ++;
1345  }
1346  }
1347  g_list_free (info->trans_list);
1348  info->trans_list = g_list_reverse (trans_list_remain);
1349  DEBUG("%d transactions remaining to process in file %s\n", g_list_length (info->trans_list),
1350  selected_filename);
1351 
1352  gnc_window_show_progress (nullptr, -1);
1353 
1354  // See whether the view has anything in it and warn the user if not.
1355  if (gnc_gen_trans_list_empty (info->gnc_ofx_importer_gui))
1356  {
1357  gnc_gen_trans_list_delete (info->gnc_ofx_importer_gui);
1358  if (info->num_trans_processed)
1359  {
1360  gnc_info_dialog (parent, _("While importing transactions from OFX file '%s' found %d previously imported transactions, no new transactions."),
1361  selected_filename,
1362  info->num_trans_processed);
1363  // This is required to ensure we don't mistakenly assume the user canceled.
1364  info->response = GTK_RESPONSE_OK;
1365  gnc_ofx_match_done (NULL, info);
1366  return;
1367  }
1368  }
1369  else
1370  {
1371  /* Show the match dialog and connect to the "destroy" signal
1372  so we can trigger a reconcile when the user clicks OK when
1373  done matching transactions if required. Connecting to
1374  response isn't enough because only when the matcher is
1375  destroyed do imported transactions get recorded */
1376  g_signal_connect (G_OBJECT (gnc_gen_trans_list_widget (info->gnc_ofx_importer_gui)),
1377  "destroy",
1378  G_CALLBACK (gnc_ofx_match_done),
1379  info);
1380 
1381  // Connect to response so we know if the user pressed "cancel".
1382  g_signal_connect (G_OBJECT (gnc_gen_trans_list_widget (info->gnc_ofx_importer_gui)),
1383  "response",
1384  G_CALLBACK (gnc_ofx_on_match_click),
1385  info);
1386 
1387  gnc_gen_trans_list_show_all (info->gnc_ofx_importer_gui);
1388 
1389  // Show or hide the check box for reconciling after match,
1390  // depending on whether a statement was received.
1391  gnc_gen_trans_list_show_reconcile_after_close_button (info->gnc_ofx_importer_gui,
1392  info->statement != NULL,
1393  info->run_reconcile);
1394 
1395  // Finally connect to the reconcile after match check box so
1396  // we can be notified if the user wants/does not want to
1397  // reconcile.
1398  g_signal_connect (G_OBJECT (gnc_gen_trans_list_get_reconcile_after_close_button
1399  (info->gnc_ofx_importer_gui)),
1400  "toggled",
1401  G_CALLBACK (reconcile_when_close_toggled_cb),
1402  info);
1403  }
1404 }
1405 
1406 // Aux function to process the OFX file in info->file_list
1407 static void
1408 gnc_file_ofx_import_process_file (ofx_info* info)
1409 {
1410  LibofxContextPtr libofx_context;
1411  char* filename = NULL;
1412  char * selected_filename = NULL;
1413  GtkWindow *parent = info->parent;
1414 
1415  if (info->file_list == NULL)
1416  return;
1417 
1418  filename = static_cast<char*>(info->file_list->data);
1419  libofx_context = libofx_get_new_context();
1420 
1421 #ifdef G_OS_WIN32
1422  selected_filename = g_win32_locale_filename_from_utf8 (filename);
1423  g_free (filename);
1424 #else
1425  selected_filename = filename;
1426 #endif
1427  DEBUG("Filename found: %s", selected_filename);
1428 
1429  // Reset the reconciliation information.
1430  info->num_trans_processed = 0;
1431  info->statement = NULL;
1432 
1433  /* Initialize libofx and set the callbacks*/
1434  ofx_set_statement_cb (libofx_context, ofx_proc_statement_cb, info);
1435  ofx_set_account_cb (libofx_context, ofx_proc_account_cb, info);
1436  ofx_set_transaction_cb (libofx_context, ofx_proc_transaction_cb, info);
1437  ofx_set_security_cb (libofx_context, ofx_proc_security_cb, info);
1438  /*ofx_set_status_cb(libofx_context, ofx_proc_status_cb, 0);*/
1439 
1440  // Create the match dialog, and run the ofx file through the importer.
1441  info->gnc_ofx_importer_gui = gnc_gen_trans_list_new (GTK_WIDGET(parent), NULL, FALSE, 42, FALSE);
1442  libofx_proc_file (libofx_context, selected_filename, AUTODETECT);
1443 
1444  // Free the libofx context before recursing to process the next file
1445  libofx_free_context(libofx_context);
1446  runMatcher(info, selected_filename,true);
1447  g_free(selected_filename);
1448 }
1449 
1450 // The main import function. Starts the chain of file imports (if there are several)
1451 void gnc_file_ofx_import (GtkWindow *parent)
1452 {
1453  extern int ofx_PARSER_msg;
1454  extern int ofx_DEBUG_msg;
1455  extern int ofx_WARNING_msg;
1456  extern int ofx_ERROR_msg;
1457  extern int ofx_INFO_msg;
1458  extern int ofx_STATUS_msg;
1459  GSList* selected_filenames = NULL;
1460  char *default_dir;
1461  GList *filters = NULL;
1462  ofx_info* info = NULL;
1463  GtkFileFilter* filter = gtk_file_filter_new ();
1464 
1465 
1466  ofx_PARSER_msg = false;
1467  ofx_DEBUG_msg = false;
1468  ofx_WARNING_msg = true;
1469  ofx_ERROR_msg = true;
1470  ofx_INFO_msg = true;
1471  ofx_STATUS_msg = false;
1472 
1473  DEBUG("gnc_file_ofx_import(): Begin...\n");
1474 
1475  default_dir = gnc_get_default_directory(GNC_PREFS_GROUP);
1476  gtk_file_filter_set_name (filter, _("Open/Quicken Financial Exchange file (*.ofx, *.qfx)"));
1477  gtk_file_filter_add_pattern (filter, "*.[oqOQ][fF][xX]");
1478  filters = g_list_prepend( filters, filter );
1479 
1480  selected_filenames = gnc_file_dialog_multi (parent,
1481  _("Select one or multiple OFX/QFX file(s) to process"),
1482  filters,
1483  default_dir,
1484  GNC_FILE_DIALOG_IMPORT);
1485  g_free(default_dir);
1486 
1487  if (selected_filenames)
1488  {
1489  /* Remember the directory as the default. */
1490  default_dir = g_path_get_dirname(static_cast<char*>(selected_filenames->data));
1491  gnc_set_default_directory(GNC_PREFS_GROUP, default_dir);
1492  g_free(default_dir);
1493 
1494  /* Look up the needed preferences */
1495  auto_create_commodity =
1496  gnc_prefs_get_bool (GNC_PREFS_GROUP_IMPORT, GNC_PREF_AUTO_COMMODITY);
1497 
1498  DEBUG("Opening selected file(s)");
1499  // Create the structure that holds the list of files to process and the statement info.
1500  info = g_new(ofx_info,1);
1501  info->num_trans_processed = 0;
1502  info->statement = NULL;
1503  info->last_investment_account = NULL;
1504  info->last_import_account = NULL;
1505  info->last_income_account = NULL;
1506  info->parent = parent;
1507  info->run_reconcile = FALSE;
1508  info->file_list = selected_filenames;
1509  info->trans_list = NULL;
1510  info->response = 0;
1511  // Call the aux import function.
1512  gnc_file_ofx_import_process_file (info);
1513  }
1514 }
1515 
1516 
void xaccSplitSetValue(Split *split, gnc_numeric val)
The xaccSplitSetValue() method sets the value of this split in the transaction&#39;s commodity.
Definition: gmock-Split.cpp:92
gnc_commodity * gnc_commodity_table_insert(gnc_commodity_table *table, gnc_commodity *comm)
Add a new commodity to the commodity table.
Account * gnc_account_get_parent(const Account *acc)
This routine returns a pointer to the parent of the specified account.
Definition: Account.cpp:2916
#define xaccTransAppendSplit(t, s)
Add a split to the transaction.
Definition: Transaction.h:380
Transaction * xaccMallocTransaction(QofBook *book)
The xaccMallocTransaction() will malloc memory and initialize it.
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:1320
void gnc_gen_trans_list_show_reconcile_after_close_button(GNCImportMainMatcher *info, bool reconcile_after_close, bool active)
Show and set the reconcile after close check button.
void xaccTransSetDatePostedSecsNormalized(Transaction *trans, time64 time)
This function sets the posted date of the transaction, specified by a time64 (see ctime(3))...
int gnc_commodity_get_fraction(const gnc_commodity *cm)
Retrieve the fraction for the specified commodity.
void qof_instance_get(const QofInstance *inst, const gchar *first_prop,...)
Wrapper for g_object_get.
Split * xaccTransGetSplit(const Transaction *trans, int i)
Return a pointer to the indexed split in this transaction&#39;s split list.
time64 xaccTransGetDate(const Transaction *trans)
Retrieve the posted date of the transaction.
gboolean gnc_account_is_root(const Account *account)
This routine indicates whether the specified account is the root node of an account tree...
Definition: Account.cpp:2934
This quote source pulls from a single specific web site.
This file contains the functions to present a gui to the user for creating a new account or editing a...
gnc_numeric double_to_gnc_numeric(double n, gint64 denom, gint how)
Convert a floating-point number to a gnc_numeric.
QofBook * qof_instance_get_book(gconstpointer inst)
Return the book pointer.
void gnc_gen_trans_list_show_all(GNCImportMainMatcher *info)
Shows widgets.
gnc_commodity * gnc_import_select_commodity(const char *cusip, gboolean ask_on_unknown, const char *default_fullname, const char *default_mnemonic)
Must be called with a string containing a unique identifier for the commodity.
GtkWindow * gnc_ui_get_main_window(GtkWidget *widget)
Get a pointer to the final GncMainWindow widget is rooted in.
utility functions for the GnuCash UI
#define G_LOG_DOMAIN
Functions providing the SX List as a plugin page.
GNCAccountType xaccAccountGetType(const Account *acc)
Returns the account&#39;s account type.
Definition: Account.cpp:3248
int xaccAccountGetCommoditySCU(const Account *acc)
Return the SCU for the account.
Definition: Account.cpp:2726
void xaccTransSetNotes(Transaction *trans, const char *notes)
Sets the transaction Notes.
STRUCTS.
#define DEBUG(format, args...)
Print a debugging message.
Definition: qoflog.h:264
Functions that are supported by all types of windows.
void qof_instance_set(QofInstance *inst, const gchar *first_prop,...)
Wrapper for g_object_set Group setting multiple parameters in a single begin/commit/rollback.
#define GNC_PREFS_GROUP_IMPORT
The preferences used by the importer.
void xaccAccountSetOnlineID(Account *acc, const char *id)
Set the account&#39;s online_id, the identifier (e.g.
Definition: Account.cpp:2642
void xaccTransSetDescription(Transaction *trans, const char *desc)
Sets the transaction Description.
Transaction matcher main window.
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.
Transaction * xaccSplitGetParent(const Split *split)
Returns the parent transaction of the split.
Ofx import module interface.
Generic and very flexible account matcher/picker.
gchar * gnc_numeric_to_string(gnc_numeric n)
Convert to string.
gnc_quote_source * gnc_quote_source_lookup_by_ti(QuoteSourceType type, gint index)
Given the type/index of a quote source, find the data structure identified by this pair...
#define PERR(format, args...)
Log a serious error.
Definition: qoflog.h:244
void gnc_commodity_user_set_quote_flag(gnc_commodity *cm, const gboolean flag)
Set the automatic price quote flag for the specified commodity, based on user input.
void gnc_gen_trans_list_add_trans(GNCImportMainMatcher *gui, Transaction *trans)
Add a newly imported Transaction to the Transaction Importer.
struct tm * gnc_localtime_r(const time64 *secs, struct tm *time)
fill out a time struct from a 64-bit time value adjusted for the current time zone.
Definition: gnc-date.cpp:115
void gnc_file_ofx_import(GtkWindow *parent)
The gnc_file_ofx_import() routine will pop up a standard file selection dialogue asking the user to p...
gnc_numeric gnc_numeric_reduce(gnc_numeric n)
Return input after reducing it by Greater Common Factor (GCF) elimination.
void xaccTransSetCurrency(Transaction *trans, gnc_commodity *curr)
Set a new currency on a transaction.
void xaccTransDestroy(Transaction *trans)
Destroys a transaction.
bool gnc_gen_trans_list_empty(GNCImportMainMatcher *info)
Checks whether there are no transactions to match.
gboolean qof_log_check(QofLogModule domain, QofLogLevel level)
Check to see if the given log_module is configured to log at the given log_level. ...
Definition: qoflog.cpp:330
Stock accounts will typically be shown in registers which show three columns: price, number of shares, and value.
Definition: Account.h:122
int xaccTransCountSplits(const Transaction *trans)
Returns the number of splits in this transaction.
#define xaccAccountGetGUID(X)
Definition: Account.h:252
char * qof_print_date(time64 secs)
Convenience; calls through to qof_print_date_dmy_buff().
Definition: gnc-date.cpp:610
void gnc_commodity_set_quote_source(gnc_commodity *cm, gnc_quote_source *src)
Set the automatic price quote source for the specified commodity.
void xaccSplitSetAmount(Split *split, gnc_numeric amt)
The xaccSplitSetAmount() method sets the amount in the account&#39;s commodity that the split should have...
Definition: gmock-Split.cpp:77
gchar * gnc_account_get_full_name(const Account *account)
The gnc_account_get_full_name routine returns the fully qualified name of the account using the given...
Definition: Account.cpp:3286
Account handling public routines.
GtkWidget * gnc_gen_trans_list_get_reconcile_after_close_button(GNCImportMainMatcher *info)
Returns the reconcile after close check button.
gboolean xaccAccountTypesCompatible(GNCAccountType parent_type, GNCAccountType child_type)
Return TRUE if accounts of type parent_type can have accounts of type child_type as children...
Definition: Account.cpp:4427
GtkWidget * gnc_gen_trans_list_widget(GNCImportMainMatcher *info)
Returns the widget of this dialog.
Income accounts are used to denote income.
Definition: Account.h:140
void xaccSplitSetMemo(Split *split, const char *memo)
The memo is an arbitrary string associated with a split.
gnc_commodity * gnc_commodity_new(QofBook *book, const char *fullname, const char *name_space, const char *mnemonic, const char *cusip, int fraction)
Create a new commodity.
line of credit – don&#39;t use this for now, see NUM_ACCOUNT_TYPES
Definition: Account.h:171
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.
The bank account type denotes a savings or checking account held at a bank.
Definition: Account.h:107
const char * gnc_commodity_get_fullname(const gnc_commodity *cm)
Retrieve the full name for the specified commodity.
void gnc_utf8_strip_invalid(gchar *str)
Strip any non-UTF-8 characters from a string.
gnc_numeric gnc_numeric_abs(gnc_numeric a)
Returns a newly created gnc_numeric that is the absolute value of the given gnc_numeric value...
#define MAX_DATE_LENGTH
The maximum length of a string created by the date printers.
Definition: gnc-date.h:108
void xaccTransCommitEdit(Transaction *trans)
The xaccTransCommitEdit() method indicates that the changes to the transaction and its splits are com...
void xaccTransBeginEdit(Transaction *trans)
The xaccTransBeginEdit() method must be called before any changes are made to a transaction or any of...
asset (and liability) accounts indicate generic, generalized accounts that are none of the above...
Definition: Account.h:116
All type declarations for the whole Gnucash engine.
GNCAccountType
The account types are used to determine how the transaction data in the account is displayed...
Definition: Account.h:101
Split * xaccMallocSplit(QofBook *book)
Constructor.
Definition: gmock-Split.cpp:37
GLib helper routines.
Generic api to store and retrieve preferences.
QofBook reference.
Definition: qofbook-p.hpp:46
gchar * gnc_utf8_strip_invalid_strdup(const gchar *str)
Returns a newly allocated copy of the given string but with any non-UTF-8 character stripped from it...
Account * gnc_ui_new_accounts_from_name_with_defaults(GtkWindow *parent, const char *name, GList *valid_types, const gnc_commodity *default_commodity, Account *parent_acct)
Display a modal window for creating a new account.
void xaccAccountBeginEdit(Account *acc)
The xaccAccountBeginEdit() subroutine is the first phase of a two-phase-commit wrapper for account up...
Definition: Account.cpp:1473
Account * xaccSplitGetAccount(const Split *split)
Returns the account of this split, which was set through xaccAccountInsertSplit().
Definition: gmock-Split.cpp:53
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Get the account&#39;s commodity.
Definition: Account.cpp:3389
gnc_commodity * xaccTransGetCurrency(const Transaction *trans)
Returns the valuation commodity of this transaction.
#define xaccAccountInsertSplit(acc, s)
The xaccAccountInsertSplit() method will insert the indicated split into the indicated account...
Definition: Account.h:1065
void gnc_gen_trans_list_delete(GNCImportMainMatcher *info)
Deletes the given object.
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.
bank account type – don&#39;t use this for now, see NUM_ACCOUNT_TYPES
Definition: Account.h:169
Preference keys for the generic importer.
Round to the nearest integer, rounding away from zero when there are two equidistant nearest integers...
Definition: gnc-numeric.h:165
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
void xaccTransSetDateEnteredSecs(Transaction *trans, time64 secs)
Modify the date of when the transaction was entered.
const char * xaccAccountGetName(const Account *acc)
Get the account&#39;s name.
Definition: Account.cpp:3270
A Generic commodity matcher/picker.
Not a type.
Definition: Account.h:105
API for Transactions and Splits (journal entries)
The type used to store guids in C.
Definition: guid.h:75
char * gnc_time64_to_iso8601_buff(time64 time, char *buff)
The gnc_time64_to_iso8601_buff() routine takes the input UTC time64 value and prints it as an ISO-860...
Definition: gnc-date.cpp:1213
void xaccAccountCommitEdit(Account *acc)
ThexaccAccountCommitEdit() subroutine is the second phase of a two-phase-commit wrapper for account u...
Definition: Account.cpp:1514
The Credit card account is used to denote credit (e.g.
Definition: Account.h:113
gnc_numeric xaccSplitGetAmount(const Split *split)
Returns the amount of the split in the account&#39;s commodity.
Definition: gmock-Split.cpp:69
Account * xaccAccountLookup(const GncGUID *guid, QofBook *book)
The xaccAccountLookup() subroutine will return the account associated with the given id...
Definition: Account.cpp:2048