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  gnc_import_set_split_online_id(split,
596  sanitize_string (data->fi_id));
597  }
598 }
599 
600 typedef struct
601 {
602  gnc_commodity *commodity;
603  char *online_id;
604  char *acct_text;
605  gboolean choosing;
607 
608 static Account*
609 create_investment_subaccount(GtkWindow *parent, Account* parent_acct,
610  InvestmentAcctData *inv_data)
611 {
612 
613  Account *investment_account =
614  gnc_ofx_new_account(parent,
615  inv_data->acct_text,
616  inv_data->commodity,
617  parent_acct,
619  if (investment_account)
620  {
621  gnc_import_set_acc_online_id(investment_account, inv_data->online_id);
622  inv_data->choosing = FALSE;
623  ofx_parent_account = parent_acct;
624  }
625  else
626  {
627  ofx_parent_account = NULL;
628  }
629  return investment_account;
630 }
631 
632 static gboolean
633 continue_account_selection(GtkWidget* parent, Account* account,
634  gnc_commodity* commodity)
635 {
636  gboolean keep_going =
637  gnc_verify_dialog(
638  GTK_WINDOW (parent), TRUE,
639  "The chosen account \"%s\" does not have the correct "
640  "currency/security \"%s\" (it has \"%s\" instead). "
641  "This account cannot be used. "
642  "Do you want to choose again?",
643  xaccAccountGetName(account),
644  gnc_commodity_get_fullname(commodity),
646  // We must also delete the online_id that was set in gnc_import_select_account()
647  gnc_import_set_acc_online_id(account, "");
648  return keep_going;
649 }
650 
651 static Account*
652 choose_investment_account_helper(OfxTransactionData *data, ofx_info *info,
653  InvestmentAcctData *inv_data)
654 {
655  Account *investment_account, *parent_account;
656 
657  if (xaccAccountGetCommodity(info->last_investment_account) == inv_data->commodity)
658  parent_account = info->last_investment_account;
659  else
660  parent_account = ofx_parent_account;
661 
662  investment_account =
663  gnc_import_select_account(GTK_WIDGET(info->parent),
664  inv_data->online_id,
665  TRUE, inv_data->acct_text,
666  inv_data->commodity, ACCT_TYPE_STOCK,
667  parent_account, &inv_data->choosing);
668  if (investment_account &&
669  xaccAccountGetCommodity(investment_account) == inv_data->commodity)
670  {
671  Account *parent_account = gnc_account_get_parent(investment_account);
672 
673  if (!ofx_parent_account && parent_account &&
674  !gnc_account_is_root(parent_account) &&
677  ofx_parent_account = parent_account;
678 
679  info->last_investment_account = investment_account;
680  return investment_account;
681  }
682 
683  /* That didn't work out. Create a subaccount if we can. */
684  if (auto_create_commodity && ofx_parent_account)
685  {
686  investment_account =
687  create_investment_subaccount(GTK_WINDOW(info->parent),
688  ofx_parent_account,
689  inv_data);
690  }
691  else
692  {
693  // No account with matching commodity. Ask the user
694  // whether to continue or abort.
695  inv_data->choosing =
696  continue_account_selection(GTK_WIDGET(info->parent),
697  investment_account, inv_data->commodity);
698  investment_account = NULL;
699  }
700 
701  return investment_account;
702 }
703 
704 static Account*
705 choose_investment_account(OfxTransactionData *data, ofx_info *info,
706  gnc_commodity *commodity)
707 {
708  Account* investment_account = NULL;
709  InvestmentAcctData inv_data = {commodity, NULL, NULL, TRUE};
710 
711  // As we now have the commodity, select the account with that commodity.
712 
713  /* Translators: This string is a default account name. It MUST
714  * NOT contain the character ':' anywhere in it or in any
715  * translations. */
716  inv_data.acct_text = g_strdup_printf(
717  _("Stock account for security \"%s\""),
718  sanitize_string (data->security_data_ptr->secname));
719 
720  inv_data.online_id =
721  g_strdup_printf("%s%s", data->account_id, data->unique_id);
722 
723  // Loop until we either have an account, or the user pressed Cancel
724  while (!investment_account && inv_data.choosing)
725  investment_account = choose_investment_account_helper(data, info,
726  &inv_data);
727  if (!investment_account)
728  {
729  PERR("No investment account found for text: %s\n", inv_data.acct_text);
730  }
731  g_free (inv_data.acct_text);
732  g_free (inv_data.online_id);
733 
734  return investment_account;
735 }
736 
737 static Account*
738 choose_income_account(Account* investment_account, Transaction *transaction,
739  OfxTransactionData *data, ofx_info *info)
740 {
741  Account *income_account = NULL;
742  DEBUG("Now let's find an account for the destination split");
743  income_account =
744  get_associated_income_account(investment_account);
745 
746  if (income_account == NULL)
747  {
748  char *income_account_text;
749  gnc_commodity *currency = xaccTransGetCurrency(transaction);
750  DEBUG("Couldn't find an associated income account");
751  /* Translators: This string is a default account
752  * name. It MUST NOT contain the character ':' anywhere
753  * in it or in any translations. */
754  income_account_text = g_strdup_printf(
755  _("Income account for security \"%s\""),
756  sanitize_string (data->security_data_ptr->secname));
757  income_account =
758  gnc_import_select_account(GTK_WIDGET(info->parent), NULL, TRUE,
759  income_account_text, currency,
761  info->last_income_account, NULL);
762 
763  if (income_account != NULL)
764  {
765  info->last_income_account = income_account;
766  set_associated_income_account(investment_account,
767  income_account);
768  DEBUG("KVP written");
769  }
770  }
771  else
772  {
773  DEBUG("Found at least one associated income account");
774  }
775 
776  return income_account;
777 }
778 
779 static void
780 add_investment_split(Transaction* transaction, Account* account,
781  OfxTransactionData *data)
782 {
783  Split *split;
784  QofBook *book = gnc_account_get_book(account);
785  gnc_numeric gnc_amount, gnc_units;
786  gnc_commodity *commodity = xaccAccountGetCommodity(account);
787  DEBUG("Adding investment split; Money flows from or into the stock account");
788  split = xaccMallocSplit(book);
789  xaccTransAppendSplit(transaction, split);
790  xaccAccountInsertSplit(account, split);
791 
792  gnc_amount =
793  gnc_ofx_numeric_from_double_txn(ofx_get_investment_amount(data),
794  transaction);
795  gnc_units = gnc_ofx_numeric_from_double (data->units, commodity);
796  xaccSplitSetAmount(split, gnc_units);
797  xaccSplitSetValue(split, gnc_amount);
798 
799  /* set tran-num and/or split-action per book option */
800  if (data->check_number_valid)
801  {
802  gnc_set_num_action(transaction, split, data->check_number, NULL);
803  }
804  else if (data->reference_number_valid)
805  {
806  gnc_set_num_action(transaction, split,
807  data->reference_number, NULL);
808  }
809  if (data->security_data_ptr->memo_valid)
810  {
811  xaccSplitSetMemo(split,
812  sanitize_string (data->security_data_ptr->memo));
813  }
814  if (data->fi_id_valid &&
817  {
818  gnc_import_set_split_online_id(split,
819  sanitize_string (data->fi_id));
820  }
821 }
822 
823 static void
824 add_currency_split(Transaction *transaction, Account* account,
825  double amount, OfxTransactionData *data)
826 {
827  Split *split;
828  QofBook *book = gnc_account_get_book(account);
829  gnc_numeric gnc_amount;
830 
831  split = xaccMallocSplit(book);
832  xaccTransAppendSplit(transaction, split);
833  xaccAccountInsertSplit(account, split);
834  gnc_amount = gnc_ofx_numeric_from_double_txn(amount, transaction);
835  xaccSplitSetBaseValue(split, gnc_amount, xaccTransGetCurrency(transaction));
836 
837  // Set split memo from ofx transaction name or memo
838  gnc_ofx_set_split_memo(data, split);
839  if (data->fi_id_valid)
840  gnc_import_set_split_online_id (split, sanitize_string (data->fi_id));
841 }
842 
843 /* ******** Process an investment transaction **********/
844 /* Note that the ACCT_TYPE_STOCK account type
845  should be replaced with something derived from
846  data->invtranstype*/
847 
848 static void
849 process_investment_transaction(Transaction *transaction, Account *import_account,
850  OfxTransactionData *data, ofx_info *info)
851 {
852  Account *investment_account = NULL;
853  Account *income_account = NULL;
854  gnc_commodity *investment_commodity;
855  double amount = data->amount;
856 
857  g_return_if_fail(data->invtransactiontype_valid);
858 
859  gnc_utf8_strip_invalid (data->unique_id);
860 
861 
862  // Set the cash split unless it's a reinvestment, which doesn't have one.
863  if (data->invtransactiontype != OFX_REINVEST)
864  {
865  DEBUG("Adding investment cash split.");
866  add_currency_split(transaction, import_account,
867  -ofx_get_investment_amount(data), data);
868  }
869 
870  investment_commodity = gnc_import_select_commodity(data->unique_id,
871  FALSE, NULL, NULL);
872  if (!investment_commodity)
873  {
874  PERR("Commodity not found for the investment transaction");
875  return;
876  }
877  investment_account = choose_investment_account(data, info,
878  investment_commodity);
879 
880  if (!investment_account)
881  {
882  PERR("Failed to determine an investment asset account.");
883  return;
884  }
885 
886  if (data->invtransactiontype != OFX_INCOME)
887  {
888  if (data->unitprice_valid && data->units_valid)
889  add_investment_split(transaction, investment_account, data);
890  else
891  PERR("Unable to add investment split, unit price or units were invalid.");
892  }
893 
894  if (!(data->invtransactiontype == OFX_REINVEST
895  || data->invtransactiontype == OFX_INCOME))
896  //Done
897  return;
898 
899 #ifdef HAVE_LIBOFX_VERSION_0_10
900  if (data->currency_ratio_valid && data->currency_ratio != 0)
901  amount *= data->currency_ratio;
902 #endif
903  income_account = choose_income_account(investment_account,
904  transaction, data, info);
905  g_return_if_fail(income_account);
906 
907  DEBUG("Adding investment income split.");
908  if (data->invtransactiontype == OFX_REINVEST)
909  add_currency_split(transaction, income_account, amount, data);
910  else
911  add_currency_split(transaction, income_account, -amount, data);
912 }
913 
914 int ofx_proc_transaction_cb(OfxTransactionData data, void *user_data)
915 {
916  Account *import_account;
917  gnc_commodity *currency = NULL;
918  QofBook *book;
919  Transaction *transaction;
920  ofx_info* info = (ofx_info*) user_data;
921 
922  g_assert(info->parent);
923 
924  if (!data.amount_valid)
925  {
926  PERR("The transaction doesn't have a valid amount");
927  return 0;
928  }
929 
930  if (!data.account_id_valid)
931  {
932  PERR("account ID for this transaction is unavailable!");
933  return 0;
934  }
935 
936  gnc_utf8_strip_invalid (data.account_id);
937 
938  import_account = gnc_import_select_account(GTK_WIDGET(info->parent),
939  data.account_id,
940  0, NULL, NULL, ACCT_TYPE_NONE,
941  info->last_import_account, NULL);
942  if (import_account == NULL)
943  {
944  PERR("Unable to find account for id %s", data.account_id);
945  return 0;
946  }
947  info->last_import_account = import_account;
948  /***** Validate the input strings to ensure utf8 *****/
949  if (data.name_valid)
950  gnc_utf8_strip_invalid(data.name);
951  if (data.memo_valid)
952  gnc_utf8_strip_invalid(data.memo);
953  if (data.check_number_valid)
954  gnc_utf8_strip_invalid(data.check_number);
955  if (data.reference_number_valid)
956  gnc_utf8_strip_invalid(data.reference_number);
957 
958  /***** Create the transaction and setup transaction data *******/
959  book = gnc_account_get_book(import_account);
960  transaction = xaccMallocTransaction(book);
961  xaccTransBeginEdit(transaction);
962 
963  set_transaction_dates(transaction, &data);
964  fill_transaction_description(transaction, &data);
965  fill_transaction_notes(transaction, &data);
966 
967  if (data.account_ptr && data.account_ptr->currency_valid)
968  {
969  DEBUG("Currency from libofx: %s", data.account_ptr->currency);
970  currency = gnc_commodity_table_lookup( gnc_get_current_commodities (),
971  GNC_COMMODITY_NS_CURRENCY,
972  data.account_ptr->currency);
973  }
974  else
975  {
976  DEBUG("Currency from libofx unavailable, defaulting to account's default");
977  currency = xaccAccountGetCommodity(import_account);
978  }
979 
980  xaccTransSetCurrency(transaction, currency);
981 
982  if (!data.invtransactiontype_valid
983 #ifdef HAVE_LIBOFX_VERSION_0_10
984  || data.invtransactiontype == OFX_INVBANKTRAN
985 #endif
986  )
987  process_bank_transaction(transaction, import_account, &data, info);
988  else if (data.unique_id_valid
989  && data.security_data_valid
990  && data.security_data_ptr != NULL
991  && data.security_data_ptr->secname_valid)
992  process_investment_transaction(transaction, import_account,
993  &data, info);
994  else
995  {
996  PERR("Unsupported OFX transaction type.");
997  xaccTransDestroy(transaction);
998  xaccTransCommitEdit(transaction);
999  return 0;
1000  }
1001 
1002  /* Send transaction to importer GUI. */
1003  if (xaccTransCountSplits(transaction) > 0)
1004  {
1005  DEBUG("%d splits sent to the importer gui",
1006  xaccTransCountSplits(transaction));
1007  info->trans_list = g_list_prepend (info->trans_list, transaction);
1008  }
1009  else
1010  {
1011  PERR("No splits in transaction (missing account?), ignoring.");
1012  xaccTransDestroy(transaction);
1013  xaccTransCommitEdit(transaction);
1014  }
1015 
1016  info->num_trans_processed += 1;
1017  return 0;
1018 }//end ofx_proc_transaction()
1019 
1020 
1021 int ofx_proc_statement_cb (struct OfxStatementData data, void * statement_user_data)
1022 {
1023  ofx_info* info = (ofx_info*) statement_user_data;
1024  struct OfxStatementData *statement = g_new (struct OfxStatementData, 1);
1025  *statement = data;
1026  info->statement = g_list_prepend (info->statement, statement);
1027  return 0;
1028 }
1029 
1030 
1031 int ofx_proc_account_cb(struct OfxAccountData data, void * account_user_data)
1032 {
1033  gnc_commodity_table * commodity_table;
1034  gnc_commodity * default_commodity;
1035  GNCAccountType default_type = ACCT_TYPE_NONE;
1036  gchar * account_description;
1037  GtkWidget * main_widget;
1038  GtkWidget * parent;
1039  /* In order to trigger a book options display on the creation of a new book,
1040  * we need to detect when we are dealing with a new book. */
1041  gboolean new_book = gnc_is_new_book();
1042  ofx_info* info = (ofx_info*) account_user_data;
1043  Account* account = NULL;
1044 
1045  const gchar * account_type_name = _("Unknown OFX account");
1046 
1047  if (data.account_id_valid)
1048  {
1049  commodity_table = gnc_get_current_commodities ();
1050  if (data.currency_valid)
1051  {
1052  DEBUG("Currency from libofx: %s", data.currency);
1053  default_commodity = gnc_commodity_table_lookup(commodity_table,
1054  GNC_COMMODITY_NS_CURRENCY,
1055  data.currency);
1056  }
1057  else
1058  {
1059  default_commodity = NULL;
1060  }
1061 
1062  if (data.account_type_valid)
1063  {
1064  switch (data.account_type)
1065  {
1066  case OfxAccountData::OFX_CHECKING:
1067  default_type = ACCT_TYPE_BANK;
1068  account_type_name = _("Unknown OFX checking account");
1069  break;
1070  case OfxAccountData::OFX_SAVINGS:
1071  default_type = ACCT_TYPE_BANK;
1072  account_type_name = _("Unknown OFX savings account");
1073  break;
1074  case OfxAccountData::OFX_MONEYMRKT:
1075  default_type = ACCT_TYPE_MONEYMRKT;
1076  account_type_name = _("Unknown OFX money market account");
1077  break;
1078  case OfxAccountData::OFX_CREDITLINE:
1079  default_type = ACCT_TYPE_CREDITLINE;
1080  account_type_name = _("Unknown OFX credit line account");
1081  break;
1082  case OfxAccountData::OFX_CMA:
1083  default_type = ACCT_TYPE_NONE;
1084  /* Cash Management Account */
1085  account_type_name = _("Unknown OFX CMA account");
1086  break;
1087  case OfxAccountData::OFX_CREDITCARD:
1088  default_type = ACCT_TYPE_CREDIT;
1089  account_type_name = _("Unknown OFX credit card account");
1090  break;
1091  case OfxAccountData::OFX_INVESTMENT:
1092  default_type = ACCT_TYPE_BANK;
1093  account_type_name = _("Unknown OFX investment account");
1094  break;
1095  default:
1096  PERR("WRITEME: ofx_proc_account() This is an unknown account type!");
1097  break;
1098  }
1099  }
1100 
1101  /* If the OFX importer was started in Gnucash in a 'new_book' situation,
1102  * as described above, the first time the 'ofx_proc_account_cb' function
1103  * is called a book is created. (This happens after the 'new_book' flag
1104  * is set in 'gnc_get_current_commodities', called above.) So, before
1105  * calling 'gnc_import_select_account', allow the user to set book
1106  * options. */
1107  if (new_book)
1108  gnc_new_book_option_display (GTK_WIDGET (gnc_ui_get_main_window (NULL)));
1109 
1110  gnc_utf8_strip_invalid(data.account_name);
1111  gnc_utf8_strip_invalid(data.account_id);
1112  account_description = g_strdup_printf (/* This string is a default account
1113  name. It MUST NOT contain the
1114  character ':' anywhere in it or
1115  in any translation. */
1116  "%s \"%s\"",
1117  account_type_name,
1118  data.account_name);
1119 
1120  main_widget = gnc_gen_trans_list_widget (info->gnc_ofx_importer_gui);
1121 
1122  /* On first use, the import-main-matcher is hidden / not realized so to
1123  * get a parent use the transient parent of the matcher */
1124  if (gtk_widget_get_realized (main_widget))
1125  parent = main_widget;
1126  else
1127  parent = GTK_WIDGET(gtk_window_get_transient_for (GTK_WINDOW(main_widget)));
1128 
1129  account = gnc_import_select_account (parent,
1130  data.account_id, 1,
1131  account_description, default_commodity,
1132  default_type, NULL, NULL);
1133 
1134  if (account)
1135  {
1136  info->last_import_account = account;
1137  }
1138 
1139  g_free(account_description);
1140  }
1141  else
1142  {
1143  PERR("account online ID not available");
1144  }
1145 
1146  return 0;
1147 }
1148 
1149 double ofx_get_investment_amount(const OfxTransactionData* data)
1150 {
1151  double amount = data->amount;
1152 #ifdef HAVE_LIBOFX_VERSION_0_10
1153  if (data->invtransactiontype == OFX_INVBANKTRAN)
1154  return 0.0;
1155  if (data->currency_ratio_valid && data->currency_ratio != 0)
1156  amount *= data->currency_ratio;
1157 #endif
1158  g_assert(data);
1159  switch (data->invtransactiontype)
1160  {
1161  case OFX_BUYDEBT:
1162  case OFX_BUYMF:
1163  case OFX_BUYOPT:
1164  case OFX_BUYOTHER:
1165  case OFX_BUYSTOCK:
1166  return fabs(amount);
1167  case OFX_SELLDEBT:
1168  case OFX_SELLMF:
1169  case OFX_SELLOPT:
1170  case OFX_SELLOTHER:
1171  case OFX_SELLSTOCK:
1172  return -1 * fabs(amount);
1173  default:
1174  return -1 * amount;
1175  }
1176 }
1177 
1178 // Forward declaration, required because several static functions depend on one-another.
1179 static void
1180 gnc_file_ofx_import_process_file (ofx_info* info);
1181 
1182 // gnc_ofx_process_next_file processes the next file in the info->file_list.
1183 static void
1184 gnc_ofx_process_next_file (GtkDialog *dialog, gpointer user_data)
1185 {
1186  ofx_info* info = (ofx_info*) user_data;
1187  // Free the statement (if it was allocated)
1188  g_list_free_full (info->statement, g_free);
1189  info->statement = NULL;
1190 
1191  // Done with the previous OFX file, process the next one if any.
1192  info->file_list = g_slist_delete_link (info->file_list, info->file_list);
1193  if (info->file_list)
1194  gnc_file_ofx_import_process_file (info);
1195  else
1196  {
1197  // Final cleanup.
1198  g_free (info);
1199  }
1200 }
1201 
1202 static void
1203 gnc_ofx_on_match_click (GtkDialog *dialog, gint response_id, gpointer user_data)
1204 {
1205  // Record the response of the user. If cancel we won't go to the next file, etc.
1206  ofx_info* info = (ofx_info*)user_data;
1207  info->response = response_id;
1208 }
1209 
1210 static void
1211 gnc_ofx_match_done (GtkDialog *dialog, gpointer user_data)
1212 {
1213  ofx_info* info = (ofx_info*) user_data;
1214 
1215  /* The the user did not click OK, don't process the rest of the
1216  * transaction, don't go to the next of xfile.
1217  */
1218  if (info->response != GTK_RESPONSE_OK)
1219  return;
1220 
1221  if (info->trans_list)
1222  {
1223  /* Re-run the match dialog if there are transactions
1224  * remaining in our list (happens if several accounts exist
1225  * in the same ofx).
1226  */
1227  info->gnc_ofx_importer_gui = gnc_gen_trans_list_new (GTK_WIDGET (info->parent), NULL, FALSE, 42, FALSE);
1228  runMatcher (info, NULL, true);
1229  return;
1230  }
1231 
1232  if (info->run_reconcile && info->statement && info->statement->data)
1233  {
1234  auto statement = static_cast<struct OfxStatementData*>(info->statement->data);
1235  // Open a reconcile window.
1236  Account* account = gnc_import_select_account (gnc_gen_trans_list_widget(info->gnc_ofx_importer_gui),
1237  statement->account_id,
1238  0, NULL, NULL, ACCT_TYPE_NONE, NULL, NULL);
1239  if (account && statement->ledger_balance_valid)
1240  {
1241  gnc_numeric value = double_to_gnc_numeric (statement->ledger_balance,
1242  xaccAccountGetCommoditySCU (account),
1244 
1245  RecnWindow* rec_window = recnWindowWithBalance (GTK_WIDGET (info->parent), account, value,
1246  statement->ledger_balance_date);
1247 
1248  // Connect to destroy, at which point we'll process the next OFX file..
1249  g_signal_connect (G_OBJECT (gnc_ui_reconcile_window_get_window (rec_window)), "destroy",
1250  G_CALLBACK (gnc_ofx_match_done), info);
1251  if (info->statement->next)
1252  info->statement = info->statement->next;
1253  else
1254  {
1255  g_list_free_full (g_list_first (info->statement), g_free);
1256  info->statement = NULL;
1257  }
1258  return;
1259  }
1260  }
1261  else
1262  {
1263  if (info->statement && info->statement->next)
1264  {
1265  info->statement = info->statement->next;
1266  gnc_ofx_match_done (dialog, user_data);
1267  return;
1268  }
1269  else
1270  {
1271  g_list_free_full (g_list_first (info->statement), g_free);
1272  info->statement = NULL;
1273  }
1274  }
1275  gnc_ofx_process_next_file (NULL, info);
1276 }
1277 
1278 // This callback is triggered when the user checks or unchecks the reconcile after match
1279 // check box in the matching dialog.
1280 static void
1281 reconcile_when_close_toggled_cb (GtkToggleButton *togglebutton, ofx_info* info)
1282 {
1283  info->run_reconcile = gtk_toggle_button_get_active (togglebutton);
1284 }
1285 
1286 static std::string
1287 make_date_amount_key (const Split* split)
1288 {
1289  std::ostringstream ss;
1290  auto _amount = gnc_numeric_reduce (gnc_numeric_abs (xaccSplitGetAmount (split)));
1291  ss << _amount.num << '/' << _amount.denom << ' ' << xaccTransGetDate (xaccSplitGetParent (split));
1292  return ss.str();
1293 }
1294 
1295 static void
1296 runMatcher (ofx_info* info, char * selected_filename, gboolean go_to_next_file)
1297 {
1298  GtkWindow *parent = info->parent;
1299  GList* trans_list_remain = NULL;
1300  std::unordered_map <std::string,Account*> trans_map;
1301 
1302  /* If we have multiple accounts in the ofx file, we need to
1303  * avoid processing transfers between accounts together because this will
1304  * create duplicate entries.
1305  */
1306  info->num_trans_processed = 0;
1307 
1308  gnc_window_show_progress (_("Removing duplicate transactions…"), 100);
1309 
1310  // Add transactions, but verify that there isn't one that was
1311  // already added with identical amounts and date, and a different
1312  // account. To do that, create a hash table whose key is a hash of
1313  // amount and date, and whose value is the account in which they
1314  // appear.
1315  for(GList* node = info->trans_list; node; node=node->next)
1316  {
1317  auto trans = static_cast<Transaction*>(node->data);
1318  Split* split = xaccTransGetSplit (trans, 0);
1319  Account* account = xaccSplitGetAccount (split);
1320  auto date_amount_key = make_date_amount_key (split);
1321 
1322  auto it = trans_map.find (date_amount_key);
1323  if (it != trans_map.end() && it->second != account)
1324  {
1325  if (qof_log_check (G_LOG_DOMAIN, QOF_LOG_DEBUG))
1326  {
1327  // There is a transaction with identical amounts and
1328  // dates, but a different account. That's a potential
1329  // transfer so process this transaction in a later call.
1330  gchar *name1 = gnc_account_get_full_name (account);
1331  gchar *name2 = gnc_account_get_full_name (it->second);
1332  gchar *amtstr = gnc_numeric_to_string (xaccSplitGetAmount (split));
1333  gchar *datestr = qof_print_date (xaccTransGetDate (trans));
1334  DEBUG ("Potential transfer %s %s %s %s\n", name1, name2, amtstr, datestr);
1335  g_free (name1);
1336  g_free (name2);
1337  g_free (amtstr);
1338  g_free (datestr);
1339  }
1340  trans_list_remain = g_list_prepend (trans_list_remain, trans);
1341  }
1342  else
1343  {
1344  trans_map[date_amount_key] = account;
1345  gnc_gen_trans_list_add_trans (info->gnc_ofx_importer_gui, trans);
1346  info->num_trans_processed ++;
1347  }
1348  }
1349  g_list_free (info->trans_list);
1350  info->trans_list = g_list_reverse (trans_list_remain);
1351  DEBUG("%d transactions remaining to process in file %s\n", g_list_length (info->trans_list),
1352  selected_filename);
1353 
1354  gnc_window_show_progress (nullptr, -1);
1355 
1356  // See whether the view has anything in it and warn the user if not.
1357  if (gnc_gen_trans_list_empty (info->gnc_ofx_importer_gui))
1358  {
1359  gnc_gen_trans_list_delete (info->gnc_ofx_importer_gui);
1360  if (info->num_trans_processed)
1361  {
1362  gnc_info_dialog (parent, _("While importing transactions from OFX file '%s' found %d previously imported transactions, no new transactions."),
1363  selected_filename,
1364  info->num_trans_processed);
1365  // This is required to ensure we don't mistakenly assume the user canceled.
1366  info->response = GTK_RESPONSE_OK;
1367  gnc_ofx_match_done (NULL, info);
1368  return;
1369  }
1370  }
1371  else
1372  {
1373  /* Show the match dialog and connect to the "destroy" signal
1374  so we can trigger a reconcile when the user clicks OK when
1375  done matching transactions if required. Connecting to
1376  response isn't enough because only when the matcher is
1377  destroyed do imported transactions get recorded */
1378  g_signal_connect (G_OBJECT (gnc_gen_trans_list_widget (info->gnc_ofx_importer_gui)),
1379  "destroy",
1380  G_CALLBACK (gnc_ofx_match_done),
1381  info);
1382 
1383  // Connect to response so we know if the user pressed "cancel".
1384  g_signal_connect (G_OBJECT (gnc_gen_trans_list_widget (info->gnc_ofx_importer_gui)),
1385  "response",
1386  G_CALLBACK (gnc_ofx_on_match_click),
1387  info);
1388 
1389  gnc_gen_trans_list_show_all (info->gnc_ofx_importer_gui);
1390 
1391  // Show or hide the check box for reconciling after match,
1392  // depending on whether a statement was received.
1393  gnc_gen_trans_list_show_reconcile_after_close_button (info->gnc_ofx_importer_gui,
1394  info->statement != NULL,
1395  info->run_reconcile);
1396 
1397  // Finally connect to the reconcile after match check box so
1398  // we can be notified if the user wants/does not want to
1399  // reconcile.
1400  g_signal_connect (G_OBJECT (gnc_gen_trans_list_get_reconcile_after_close_button
1401  (info->gnc_ofx_importer_gui)),
1402  "toggled",
1403  G_CALLBACK (reconcile_when_close_toggled_cb),
1404  info);
1405  }
1406 }
1407 
1408 // Aux function to process the OFX file in info->file_list
1409 static void
1410 gnc_file_ofx_import_process_file (ofx_info* info)
1411 {
1412  LibofxContextPtr libofx_context;
1413  char* filename = NULL;
1414  char * selected_filename = NULL;
1415  GtkWindow *parent = info->parent;
1416 
1417  if (info->file_list == NULL)
1418  return;
1419 
1420  filename = static_cast<char*>(info->file_list->data);
1421  libofx_context = libofx_get_new_context();
1422 
1423 #ifdef G_OS_WIN32
1424  selected_filename = g_win32_locale_filename_from_utf8 (filename);
1425  g_free (filename);
1426 #else
1427  selected_filename = filename;
1428 #endif
1429  DEBUG("Filename found: %s", selected_filename);
1430 
1431  // Reset the reconciliation information.
1432  info->num_trans_processed = 0;
1433  info->statement = NULL;
1434 
1435  /* Initialize libofx and set the callbacks*/
1436  ofx_set_statement_cb (libofx_context, ofx_proc_statement_cb, info);
1437  ofx_set_account_cb (libofx_context, ofx_proc_account_cb, info);
1438  ofx_set_transaction_cb (libofx_context, ofx_proc_transaction_cb, info);
1439  ofx_set_security_cb (libofx_context, ofx_proc_security_cb, info);
1440  /*ofx_set_status_cb(libofx_context, ofx_proc_status_cb, 0);*/
1441 
1442  // Create the match dialog, and run the ofx file through the importer.
1443  info->gnc_ofx_importer_gui = gnc_gen_trans_list_new (GTK_WIDGET(parent), NULL, FALSE, 42, FALSE);
1444  libofx_proc_file (libofx_context, selected_filename, AUTODETECT);
1445 
1446  // Free the libofx context before recursing to process the next file
1447  libofx_free_context(libofx_context);
1448  runMatcher(info, selected_filename,true);
1449  g_free(selected_filename);
1450 }
1451 
1452 // The main import function. Starts the chain of file imports (if there are several)
1453 void gnc_file_ofx_import (GtkWindow *parent)
1454 {
1455  extern int ofx_PARSER_msg;
1456  extern int ofx_DEBUG_msg;
1457  extern int ofx_WARNING_msg;
1458  extern int ofx_ERROR_msg;
1459  extern int ofx_INFO_msg;
1460  extern int ofx_STATUS_msg;
1461  GSList* selected_filenames = NULL;
1462  char *default_dir;
1463  GList *filters = NULL;
1464  ofx_info* info = NULL;
1465  GtkFileFilter* filter = gtk_file_filter_new ();
1466 
1467 
1468  ofx_PARSER_msg = false;
1469  ofx_DEBUG_msg = false;
1470  ofx_WARNING_msg = true;
1471  ofx_ERROR_msg = true;
1472  ofx_INFO_msg = true;
1473  ofx_STATUS_msg = false;
1474 
1475  DEBUG("gnc_file_ofx_import(): Begin...\n");
1476 
1477  default_dir = gnc_get_default_directory(GNC_PREFS_GROUP);
1478  gtk_file_filter_set_name (filter, _("Open/Quicken Financial Exchange file (*.ofx, *.qfx)"));
1479  gtk_file_filter_add_pattern (filter, "*.[oqOQ][fF][xX]");
1480  filters = g_list_prepend( filters, filter );
1481 
1482  selected_filenames = gnc_file_dialog_multi (parent,
1483  _("Select one or multiple OFX/QFX file(s) to process"),
1484  filters,
1485  default_dir,
1486  GNC_FILE_DIALOG_IMPORT);
1487  g_free(default_dir);
1488 
1489  if (selected_filenames)
1490  {
1491  /* Remember the directory as the default. */
1492  default_dir = g_path_get_dirname(static_cast<char*>(selected_filenames->data));
1493  gnc_set_default_directory(GNC_PREFS_GROUP, default_dir);
1494  g_free(default_dir);
1495 
1496  /* Look up the needed preferences */
1497  auto_create_commodity =
1498  gnc_prefs_get_bool (GNC_PREFS_GROUP_IMPORT, GNC_PREF_AUTO_COMMODITY);
1499 
1500  DEBUG("Opening selected file(s)");
1501  // Create the structure that holds the list of files to process and the statement info.
1502  info = g_new(ofx_info,1);
1503  info->num_trans_processed = 0;
1504  info->statement = NULL;
1505  info->last_investment_account = NULL;
1506  info->last_import_account = NULL;
1507  info->last_income_account = NULL;
1508  info->parent = parent;
1509  info->run_reconcile = FALSE;
1510  info->file_list = selected_filenames;
1511  info->trans_list = NULL;
1512  info->response = 0;
1513  // Call the aux import function.
1514  gnc_file_ofx_import_process_file (info);
1515  }
1516 }
1517 
1518 
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:2875
#define xaccTransAppendSplit(t, s)
Add a split to the transaction.
Definition: Transaction.h:381
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:2899
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 in, 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:3212
int xaccAccountGetCommoditySCU(const Account *acc)
Return the SCU for the account.
Definition: Account.cpp:2673
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 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:114
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 in)
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:324
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:248
char * qof_print_date(time64 secs)
Convenience; calls through to qof_print_date_dmy_buff().
Definition: gnc-date.cpp:608
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:3241
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:4527
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.
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:1479
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:3397
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:1048
void gnc_gen_trans_list_delete(GNCImportMainMatcher *info)
Deletes the given object.
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
Utility functions for writing import modules.
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:261
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:3234
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:1139
void xaccAccountCommitEdit(Account *acc)
ThexaccAccountCommitEdit() subroutine is the second phase of a two-phase-commit wrapper for account u...
Definition: Account.cpp:1520
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:2036