GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
gnc-ofx-import.c
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 [email protected] *
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 
35 #include <libofx/libofx.h>
36 #include "import-account-matcher.h"
38 #include "import-utilities.h"
39 #include "import-main-matcher.h"
40 
41 #include "Account.h"
42 #include "Transaction.h"
43 #include "engine-helpers.h"
44 #include "gnc-ofx-import.h"
45 #include "gnc-file.h"
46 #include "gnc-engine.h"
47 #include "gnc-ui-util.h"
48 #include "gnc-glib-utils.h"
49 #include "gnc-prefs.h"
50 #include "gnome-utils/gnc-ui.h"
52 #include "dialog-utils.h"
53 
54 #include "gnc-ofx-kvp.h"
55 
56 #define GNC_PREFS_GROUP "dialogs.import.ofx"
57 #define GNC_PREF_AUTO_COMMODITY "auto-create-commodity"
58 
59 static QofLogModule log_module = GNC_MOD_IMPORT;
60 
61 /********************************************************************\
62  * gnc_file_ofx_import
63  * Entry point
64 \********************************************************************/
65 
66 /* CS: Store the reference to the created importer gui so that the
67  ofx_proc_transaction_cb can use it. */
68 GNCImportMainMatcher *gnc_ofx_importer_gui = NULL;
69 static gboolean auto_create_commodity = FALSE;
70 static Account *ofx_parent_account = NULL;
71 
72 GList *ofx_created_commodites = NULL;
73 
74 /*
75 int ofx_proc_status_cb(struct OfxStatusData data)
76 {
77  return 0;
78 }
79 */
80 
81 int ofx_proc_security_cb(const struct OfxSecurityData data, void * security_user_data);
82 int ofx_proc_transaction_cb(struct OfxTransactionData data, void * transaction_user_data);
83 int ofx_proc_account_cb(struct OfxAccountData data, void * account_user_data);
84 static double ofx_get_investment_amount(const struct OfxTransactionData* data);
85 
86 static const gchar *gnc_ofx_ttype_to_string(TransactionType t)
87 {
88  switch (t)
89  {
90  case OFX_CREDIT:
91  return "Generic credit";
92  case OFX_DEBIT:
93  return "Generic debit";
94  case OFX_INT:
95  return "Interest earned or paid (Note: Depends on signage of amount)";
96  case OFX_DIV:
97  return "Dividend";
98  case OFX_FEE:
99  return "FI fee";
100  case OFX_SRVCHG:
101  return "Service charge";
102  case OFX_DEP:
103  return "Deposit";
104  case OFX_ATM:
105  return "ATM debit or credit (Note: Depends on signage of amount)";
106  case OFX_POS:
107  return "Point of sale debit or credit (Note: Depends on signage of amount)";
108  case OFX_XFER:
109  return "Transfer";
110  case OFX_CHECK:
111  return "Check";
112  case OFX_PAYMENT:
113  return "Electronic payment";
114  case OFX_CASH:
115  return "Cash withdrawal";
116  case OFX_DIRECTDEP:
117  return "Direct deposit";
118  case OFX_DIRECTDEBIT:
119  return "Merchant initiated debit";
120  case OFX_REPEATPMT:
121  return "Repeating payment/standing order";
122  case OFX_OTHER:
123  return "Other";
124  default:
125  return "Unknown transaction type";
126  }
127 }
128 
129 static const gchar *gnc_ofx_invttype_to_str(InvTransactionType t)
130 {
131  switch (t)
132  {
133  case OFX_BUYDEBT:
134  return "BUYDEBT (Buy debt security)";
135  case OFX_BUYMF:
136  return "BUYMF (Buy mutual fund)";
137  case OFX_BUYOPT:
138  return "BUYOPT (Buy option)";
139  case OFX_BUYOTHER:
140  return "BUYOTHER (Buy other security type)";
141  case OFX_BUYSTOCK:
142  return "BUYSTOCK (Buy stock))";
143  case OFX_CLOSUREOPT:
144  return "CLOSUREOPT (Close a position for an option)";
145  case OFX_INCOME:
146  return "INCOME (Investment income is realized as cash into the investment account)";
147  case OFX_INVEXPENSE:
148  return "INVEXPENSE (Misc investment expense that is associated with a specific security)";
149  case OFX_JRNLFUND:
150  return "JRNLFUND (Journaling cash holdings between subaccounts within the same investment account)";
151  case OFX_MARGININTEREST:
152  return "MARGININTEREST (Margin interest expense)";
153  case OFX_REINVEST:
154  return "REINVEST (Reinvestment of income)";
155  case OFX_RETOFCAP:
156  return "RETOFCAP (Return of capital)";
157  case OFX_SELLDEBT:
158  return "SELLDEBT (Sell debt security. Used when debt is sold, called, or reached maturity)";
159  case OFX_SELLMF:
160  return "SELLMF (Sell mutual fund)";
161  case OFX_SELLOPT:
162  return "SELLOPT (Sell option)";
163  case OFX_SELLOTHER:
164  return "SELLOTHER (Sell other type of security)";
165  case OFX_SELLSTOCK:
166  return "SELLSTOCK (Sell stock)";
167  case OFX_SPLIT:
168  return "SPLIT (Stock or mutial fund split)";
169  case OFX_TRANSFER:
170  return "TRANSFER (Transfer holdings in and out of the investment account)";
171  default:
172  return "ERROR, this investment transaction type is unknown. This is a bug in ofxdump";
173  }
174 
175 }
176 
177 static gchar*
178 sanitize_string (gchar* str)
179 {
180  gchar *inval;
181  const int length = -1; /*Assumes str is null-terminated */
182  while (!g_utf8_validate (str, length, (const gchar **)(&inval)))
183  *inval = '@';
184  return str;
185 }
186 
187 int ofx_proc_security_cb(const struct OfxSecurityData data, void * security_user_data)
188 {
189  char* cusip = NULL;
190  char* default_fullname = NULL;
191  char* default_mnemonic = NULL;
192 
193  if (data.unique_id_valid)
194  {
195  cusip = gnc_utf8_strip_invalid_strdup (data.unique_id);
196  }
197  if (data.secname_valid)
198  {
199  default_fullname = gnc_utf8_strip_invalid_strdup (data.secname);
200  }
201  if (data.ticker_valid)
202  {
203  default_mnemonic = gnc_utf8_strip_invalid_strdup (data.ticker);
204  }
205 
206  if (auto_create_commodity)
207  {
208  gnc_commodity *commodity =
210  FALSE,
211  default_fullname,
212  default_mnemonic);
213 
214  if (!commodity)
215  {
216  QofBook *book = gnc_get_current_book();
217  gnc_quote_source *source;
218  gint source_selection = 0; // FIXME: This is just a wild guess
219  char *commodity_namespace = NULL;
220  int fraction = 1;
221 
222  if (data.unique_id_type_valid)
223  {
224  commodity_namespace = gnc_utf8_strip_invalid_strdup (data.unique_id_type);
225  }
226 
227  g_warning("Creating a new commodity, cusip=%s", cusip);
228  /* Create the new commodity */
229  commodity = gnc_commodity_new(book,
230  default_fullname,
231  commodity_namespace,
232  default_mnemonic,
233  cusip,
234  fraction);
235 
236  /* Also set a single quote source */
237  gnc_commodity_begin_edit(commodity);
238  gnc_commodity_user_set_quote_flag (commodity, TRUE);
239  source = gnc_quote_source_lookup_by_ti (SOURCE_SINGLE, source_selection);
240  gnc_commodity_set_quote_source(commodity, source);
241  gnc_commodity_commit_edit(commodity);
242 
243  /* Remember the commodity */
244  gnc_commodity_table_insert(gnc_get_current_commodities(), commodity);
245 
246  /* Remember this new commodity for us as well */
247  ofx_created_commodites = g_list_prepend(ofx_created_commodites, commodity);
248  g_free (commodity_namespace);
249 
250  }
251  }
252  else
253  {
255  TRUE,
256  default_fullname,
257  default_mnemonic);
258  }
259 
260  g_free (cusip);
261  g_free (default_mnemonic);
262  g_free (default_fullname);
263  return 0;
264 }
265 
266 static void gnc_ofx_set_split_memo(const struct OfxTransactionData* data, Split *split)
267 {
268  g_assert(data);
269  g_assert(split);
270  /* Also put the ofx transaction name in
271  * the splits memo field, or ofx memo if
272  * name is unavailable */
273  if (data->name_valid)
274  {
275  xaccSplitSetMemo(split, data->name);
276  }
277  else if (data->memo_valid)
278  {
279  xaccSplitSetMemo(split, data->memo);
280  }
281 }
282 static gnc_numeric gnc_ofx_numeric_from_double(double value, const gnc_commodity *commodity)
283 {
284  return double_to_gnc_numeric (value,
285  gnc_commodity_get_fraction(commodity),
287 }
288 static gnc_numeric gnc_ofx_numeric_from_double_txn(double value, const Transaction* txn)
289 {
290  return gnc_ofx_numeric_from_double(value, xaccTransGetCurrency(txn));
291 }
292 
293 /* Opens the dialog to create a new account with given name, commodity, parent, type.
294  * Returns the new account, or NULL if it couldn't be created.. */
295 static Account *gnc_ofx_new_account(const char* name,
296  const gnc_commodity * account_commodity,
297  Account *parent_account,
298  GNCAccountType new_account_default_type)
299 {
300  Account *result;
301  GList * valid_types = NULL;
302 
303  g_assert(name);
304  g_assert(account_commodity);
305  g_assert(parent_account);
306 
307  if (new_account_default_type != ACCT_TYPE_NONE)
308  {
309  // Passing the types as gpointer
310  valid_types =
311  g_list_prepend(valid_types,
312  GINT_TO_POINTER(new_account_default_type));
313  if (!xaccAccountTypesCompatible(xaccAccountGetType(parent_account), new_account_default_type))
314  {
315  // Need to add the parent's account type
316  valid_types =
317  g_list_prepend(valid_types,
318  GINT_TO_POINTER(xaccAccountGetType(parent_account)));
319  }
320  }
322  valid_types,
323  account_commodity,
324  parent_account);
325  g_list_free(valid_types);
326  return result;
327 }
328 
329 
330 int ofx_proc_transaction_cb(struct OfxTransactionData data, void * transaction_user_data)
331 {
332  char dest_string[255];
333  time64 current_time = gnc_time (NULL);
334  Account *account;
335  Account *investment_account = NULL;
336  Account *income_account = NULL;
337  gchar *investment_account_text, *investment_account_onlineid;
338  gnc_commodity *currency = NULL;
339  gnc_commodity *investment_commodity = NULL;
340  gnc_numeric gnc_amount, gnc_units;
341  QofBook *book;
342  Transaction *transaction;
343  Split *split;
344  gchar *notes, *tmp;
345 
346  g_assert(gnc_ofx_importer_gui);
347 
348  if (!data.account_id_valid)
349  {
350  PERR("account ID for this transaction is unavailable!");
351  return 0;
352  }
353  else
354  gnc_utf8_strip_invalid (data.account_id);
355 
356  account = gnc_import_select_account(gnc_gen_trans_list_widget(gnc_ofx_importer_gui),
357  data.account_id,
358  0, NULL, NULL, ACCT_TYPE_NONE,
359  NULL, NULL);
360  if (account == NULL)
361  {
362  PERR("Unable to find account for id %s", data.account_id);
363  return 0;
364  }
365  /***** Validate the input strings to ensure utf8 *****/
366  if (data.name_valid)
367  gnc_utf8_strip_invalid(data.name);
368  if (data.memo_valid)
369  gnc_utf8_strip_invalid(data.memo);
370  if (data.check_number_valid)
371  gnc_utf8_strip_invalid(data.check_number);
372  if (data.reference_number_valid)
373  gnc_utf8_strip_invalid(data.reference_number);
374 
375  /***** Create the transaction and setup transaction data *******/
376  book = gnc_account_get_book(account);
377  transaction = xaccMallocTransaction(book);
378  xaccTransBeginEdit(transaction);
379 
380  /* Note: Unfortunately libofx <= 0.9.5 will not report a missing
381  * date field as an invalid one. Instead, it will report it as
382  * valid and return a completely bogus date. Starting with
383  * libofx-0.9.6 (not yet released as of 2012-09-09), it will still
384  * be reported as valid but at least the date integer itself is
385  * just plain zero. */
386  if (data.date_posted_valid && (data.date_posted != 0))
387  {
388  /* The hopeful case: We have a posted_date */
389  xaccTransSetDatePostedSecsNormalized(transaction, data.date_posted);
390  }
391  else if (data.date_initiated_valid && (data.date_initiated != 0))
392  {
393  /* No posted date? Maybe we have an initiated_date */
394  xaccTransSetDatePostedSecsNormalized(transaction, data.date_initiated);
395  }
396  else
397  {
398  /* Uh no, no valid date. As a workaround use today's date */
399  xaccTransSetDatePostedSecsNormalized(transaction, current_time);
400  }
401 
402  xaccTransSetDateEnteredSecs(transaction, current_time);
403 
404  /* Put transaction name in Description, or memo if name unavailable */
405  if (data.name_valid)
406  {
407  xaccTransSetDescription(transaction, data.name);
408  }
409  else if (data.memo_valid)
410  {
411  xaccTransSetDescription(transaction, data.memo);
412  }
413 
414  /* Put everything else in the Notes field */
415  notes = g_strdup_printf("OFX ext. info: ");
416 
417  if (data.transactiontype_valid)
418  {
419  tmp = notes;
420  notes = g_strdup_printf("%s%s%s", tmp, "|Trans type:",
421  gnc_ofx_ttype_to_string(data.transactiontype));
422  g_free(tmp);
423  }
424 
425  if (data.invtransactiontype_valid)
426  {
427  tmp = notes;
428  notes = g_strdup_printf("%s%s%s", tmp, "|Investment Trans type:",
429  gnc_ofx_invttype_to_str(data.invtransactiontype));
430  g_free(tmp);
431  }
432  if (data.memo_valid && data.name_valid) /* Copy only if memo wasn't put in Description */
433  {
434  tmp = notes;
435  notes = g_strdup_printf("%s%s%s", tmp, "|Memo:", data.memo);
436  g_free(tmp);
437  }
438  if (data.date_funds_available_valid)
439  {
440  Timespec ts;
441  timespecFromTime64(&ts, data.date_funds_available);
442  gnc_timespec_to_iso8601_buff (ts, dest_string);
443  tmp = notes;
444  notes = g_strdup_printf("%s%s%s", tmp,
445  "|Date funds available:", dest_string);
446  g_free(tmp);
447  }
448  if (data.server_transaction_id_valid)
449  {
450  tmp = notes;
451  notes = g_strdup_printf("%s%s%s", tmp,
452  "|Server trans ID (conf. number):",
453  sanitize_string (data.server_transaction_id));
454  g_free(tmp);
455  }
456  if (data.standard_industrial_code_valid)
457  {
458  tmp = notes;
459  notes = g_strdup_printf("%s%s%ld", tmp,
460  "|Standard Industrial Code:",
461  data.standard_industrial_code);
462  g_free(tmp);
463 
464  }
465  if (data.payee_id_valid)
466  {
467  tmp = notes;
468  notes = g_strdup_printf("%s%s%s", tmp, "|Payee ID:",
469  sanitize_string (data.payee_id));
470  g_free(tmp);
471  }
472 
473  //PERR("WRITEME: GnuCash ofx_proc_transaction():Add PAYEE and ADRESS here once supported by libofx! Notes=%s\n", notes);
474 
475  /* Ideally, gnucash should process the corrected transactions */
476  if (data.fi_id_corrected_valid)
477  {
478  PERR("WRITEME: GnuCash ofx_proc_transaction(): WARNING: This transaction corrected a previous transaction, but we created a new one instead!\n");
479  tmp = notes;
480  notes = g_strdup_printf("%s%s%s%s", tmp,
481  "|This corrects transaction #",
482  sanitize_string (data.fi_id_corrected),
483  "but GnuCash didn't process the correction!");
484  g_free(tmp);
485  }
486  xaccTransSetNotes(transaction, notes);
487  g_free(notes);
488 
489  if (data.account_ptr && data.account_ptr->currency_valid)
490  {
491  DEBUG("Currency from libofx: %s", data.account_ptr->currency);
492  currency = gnc_commodity_table_lookup( gnc_get_current_commodities (),
493  GNC_COMMODITY_NS_CURRENCY,
494  data.account_ptr->currency);
495  }
496  else
497  {
498  DEBUG("Currency from libofx unavailable, defaulting to account's default");
499  currency = xaccAccountGetCommodity(account);
500  }
501 
502  xaccTransSetCurrency(transaction, currency);
503  if (data.amount_valid)
504  {
505  if (!data.invtransactiontype_valid)
506  {
507  /***** Process a normal transaction ******/
508  DEBUG("Adding split; Ordinary banking transaction, money flows from or into the source account");
509  split = xaccMallocSplit(book);
510  xaccTransAppendSplit(transaction, split);
511  xaccAccountInsertSplit(account, split);
512 
513  gnc_amount = gnc_ofx_numeric_from_double_txn(data.amount, transaction);
514  xaccSplitSetBaseValue(split, gnc_amount, xaccTransGetCurrency(transaction));
515 
516  /* set tran-num and/or split-action per book option */
517  if (data.check_number_valid)
518  {
519  gnc_set_num_action(transaction, split, data.check_number, NULL);
520  }
521  else if (data.reference_number_valid)
522  {
523  gnc_set_num_action(transaction, split, data.reference_number, NULL);
524  }
525  /* Also put the ofx transaction's memo in the
526  * split's memo field */
527  if (data.memo_valid)
528  {
529  xaccSplitSetMemo(split, data.memo);
530  }
531  if (data.fi_id_valid)
532  {
533  gnc_import_set_split_online_id(split,
534  sanitize_string (data.fi_id));
535  }
536  }
537 
538  else if (data.unique_id_valid
539  && data.security_data_valid
540  && data.security_data_ptr != NULL
541  && data.security_data_ptr->secname_valid)
542  {
543  gboolean choosing_account = TRUE;
544  gnc_utf8_strip_invalid (data.unique_id);
545  /********* Process an investment transaction **********/
546  /* Note that the ACCT_TYPE_STOCK account type
547  should be replaced with something derived from
548  data.invtranstype*/
549 
550  // We have an investment transaction. First select the correct commodity.
551  investment_commodity = gnc_import_select_commodity(data.unique_id,
552  FALSE,
553  NULL,
554  NULL);
555  if (investment_commodity != NULL)
556  {
557  // As we now have the commodity, select the account with that commodity.
558 
559  investment_account_text = g_strdup_printf( /* This string is a default account
560  name. It MUST NOT contain the
561  character ':' anywhere in it or
562  in any translations. */
563  _("Stock account for security \"%s\""),
564  sanitize_string (data.security_data_ptr->secname));
565 
566  investment_account_onlineid = g_strdup_printf( "%s%s",
567  data.account_id,
568  data.unique_id);
569  investment_account = gnc_import_select_account(NULL,
570  investment_account_onlineid,
571  1,
572  investment_account_text,
573  investment_commodity,
575  NULL,
576  NULL);
577 
578  // but use it only if that's really the right commodity
579  if (investment_account
580  && xaccAccountGetCommodity(investment_account) != investment_commodity)
581  investment_account = NULL;
582 
583  // Loop until we either have an account, or the user pressed Cancel
584  while (!investment_account && choosing_account)
585  {
586  // No account with correct commodity automatically found.
587 
588  // But are we in auto-create mode and already know a parent?
589  if (auto_create_commodity && ofx_parent_account)
590  {
591  // Yes, so use that as parent when auto-creating the new account below.
592  investment_account = ofx_parent_account;
593  }
594  else
595  {
596  // Let the user choose an account
597  investment_account = gnc_import_select_account(
598  gnc_gen_trans_list_widget(gnc_ofx_importer_gui),
599  data.unique_id,
600  TRUE,
601  investment_account_text,
602  investment_commodity,
604  NULL,
605  &choosing_account);
606  }
607  // Does the chosen account have the right commodity?
608  if (investment_account && xaccAccountGetCommodity(investment_account) != investment_commodity)
609  {
610  if (auto_create_commodity
611  && xaccAccountTypesCompatible(xaccAccountGetType(investment_account),
613  {
614  // The user chose an account, but it does
615  // not have the right commodity. Also,
616  // auto-creation is on. Hence, we create a
617  // new child account of the selected one,
618  // and this one will have the right
619  // commodity.
620  Account *parent_account = investment_account;
621  investment_account =
622  gnc_ofx_new_account(investment_account_text,
623  investment_commodity,
624  parent_account,
626  if (investment_account)
627  {
628  gnc_import_set_acc_online_id(investment_account, data.unique_id);
629  choosing_account = FALSE;
630  ofx_parent_account = parent_account;
631  }
632  else
633  {
634  ofx_parent_account = NULL;
635  }
636  }
637  else
638  {
639  // No account with matching commodity. Ask the user
640  // whether to continue or abort.
641  choosing_account =
642  gnc_verify_dialog(
643  gnc_gen_trans_list_widget(gnc_ofx_importer_gui), TRUE,
644  "The chosen account \"%s\" does not have the correct "
645  "currency/security \"%s\" (it has \"%s\" instead). "
646  "This account cannot be used. "
647  "Do you want to choose again?",
648  xaccAccountGetName(investment_account),
649  gnc_commodity_get_fullname(investment_commodity),
651  // We must also delete the online_id that was set in gnc_import_select_account()
652  gnc_import_set_acc_online_id(investment_account, "");
653  investment_account = NULL;
654  }
655  }
656  }
657  if (!investment_account)
658  {
659  PERR("No investment account found for text: %s\n", investment_account_text);
660  }
661  g_free (investment_account_text);
662  g_free (investment_account_onlineid);
663  investment_account_text = NULL;
664 
665  if (investment_account != NULL &&
666  data.unitprice_valid &&
667  data.units_valid &&
668  ( data.invtransactiontype != OFX_INCOME ) )
669  {
670  DEBUG("Adding investment split; Money flows from or into the stock account");
671  split = xaccMallocSplit(book);
672  xaccTransAppendSplit(transaction, split);
673  xaccAccountInsertSplit(investment_account, split);
674 
675  gnc_amount = gnc_ofx_numeric_from_double_txn (ofx_get_investment_amount(&data),
676  transaction);
677  gnc_units = gnc_ofx_numeric_from_double (data.units, investment_commodity);
678  xaccSplitSetAmount(split, gnc_units);
679  xaccSplitSetValue(split, gnc_amount);
680 
681  /* set tran-num and/or split-action per book option */
682  if (data.check_number_valid)
683  {
684  gnc_set_num_action(transaction, split, data.check_number, NULL);
685  }
686  else if (data.reference_number_valid)
687  {
688  gnc_set_num_action(transaction, split,
689  data.reference_number, NULL);
690  }
691  if (data.security_data_ptr->memo_valid)
692  {
693  xaccSplitSetMemo(split,
694  sanitize_string (data.security_data_ptr->memo));
695  }
696  if (data.fi_id_valid)
697  {
698  gnc_import_set_split_online_id(split,
699  sanitize_string (data.fi_id));
700  }
701  }
702  else
703  {
704  if (investment_account)
705  PERR("The investment account, units or unitprice was not found for the investment transaction");
706  }
707  }
708  else
709  {
710  PERR("Commodity not found for the investment transaction");
711  }
712 
713  if (data.invtransactiontype_valid && investment_account)
714  {
715  if (data.invtransactiontype == OFX_REINVEST
716  || data.invtransactiontype == OFX_INCOME)
717  {
718  DEBUG("Now let's find an account for the destination split");
719 
720  income_account = gnc_ofx_kvp_get_assoc_account(investment_account);
721 
722  if (income_account == NULL)
723  {
724  DEBUG("Couldn't find an associated income account");
725  investment_account_text = g_strdup_printf( /* This string is a default account
726  name. It MUST NOT contain the
727  character ':' anywhere in it or
728  in any translations. */
729  _("Income account for security \"%s\""),
730  sanitize_string (data.security_data_ptr->secname));
731  income_account = gnc_import_select_account(
732  gnc_gen_trans_list_widget(gnc_ofx_importer_gui),
733  NULL,
734  1,
735  investment_account_text,
736  currency,
738  NULL,
739  NULL);
740  if (income_account != NULL)
741  {
742  gnc_ofx_kvp_set_assoc_account(investment_account,
743  income_account);
744  DEBUG("KVP written");
745  }
746 
747  }
748  else
749  {
750  DEBUG("Found at least one associated income account");
751  }
752  }
753  if (income_account != NULL &&
754  data.invtransactiontype == OFX_REINVEST)
755  {
756  DEBUG("Adding investment split; Money flows from the income account");
757  split = xaccMallocSplit(book);
758  xaccTransAppendSplit(transaction, split);
759  xaccAccountInsertSplit(income_account, split);
760 
761  gnc_amount = gnc_ofx_numeric_from_double_txn (data.amount, transaction);
762  xaccSplitSetBaseValue(split, gnc_amount, xaccTransGetCurrency(transaction));
763 
764  // Set split memo from ofx transaction name or memo
765  gnc_ofx_set_split_memo(&data, split);
766  }
767  if (income_account != NULL &&
768  data.invtransactiontype == OFX_INCOME)
769  {
770  DEBUG("Adding investment split; Money flows from the income account");
771  split = xaccMallocSplit(book);
772  xaccTransAppendSplit(transaction, split);
773  xaccAccountInsertSplit(income_account, split);
774 
775  gnc_amount = gnc_ofx_numeric_from_double_txn (-data.amount,/*OFX_INCOME amounts come in as positive numbers*/
776  transaction);
777  xaccSplitSetBaseValue(split, gnc_amount, xaccTransGetCurrency(transaction));
778 
779  // Set split memo from ofx transaction name or memo
780  gnc_ofx_set_split_memo(&data, split);
781  }
782  }
783 
784  if (data.invtransactiontype_valid
785  && data.invtransactiontype != OFX_REINVEST)
786  {
787  DEBUG("Adding investment split; Money flows from or to the cash account");
788  split = xaccMallocSplit(book);
789  xaccTransAppendSplit(transaction, split);
790  xaccAccountInsertSplit(account, split);
791 
792  gnc_amount = gnc_ofx_numeric_from_double_txn(
793  -ofx_get_investment_amount(&data), transaction);
794  xaccSplitSetBaseValue(split, gnc_amount,
795  xaccTransGetCurrency(transaction));
796 
797  // Set split memo from ofx transaction name or memo
798  gnc_ofx_set_split_memo(&data, split);
799  }
800  }
801 
802  /* Send transaction to importer GUI. */
803  if (xaccTransCountSplits(transaction) > 0)
804  {
805  DEBUG("%d splits sent to the importer gui", xaccTransCountSplits(transaction));
806  gnc_gen_trans_list_add_trans (gnc_ofx_importer_gui, transaction);
807  }
808  else
809  {
810  PERR("No splits in transaction (missing account?), ignoring.");
811  xaccTransDestroy(transaction);
812  xaccTransCommitEdit(transaction);
813  }
814  }
815  else
816  {
817  PERR("The transaction doesn't have a valid amount");
818  xaccTransDestroy(transaction);
819  xaccTransCommitEdit(transaction);
820  }
821 
822  return 0;
823 }//end ofx_proc_transaction()
824 
825 /*
826 int ofx_proc_statement_cb(struct OfxStatementData data, void * statement_user_data)
827 {
828  return 0;
829 }//end ofx_proc_statement()
830 */
831 
832 int ofx_proc_account_cb(struct OfxAccountData data, void * account_user_data)
833 {
834  gnc_commodity_table * commodity_table;
835  gnc_commodity * default_commodity;
836  GNCAccountType default_type = ACCT_TYPE_NONE;
837  gchar * account_description;
838  /* In order to trigger a book options display on the creation of a new book,
839  * we need to detect when we are dealing with a new book. */
840  gboolean new_book = gnc_is_new_book();
841 
842  const gchar * account_type_name = _("Unknown OFX account");
843 
844  if (data.account_id_valid)
845  {
846  commodity_table = gnc_get_current_commodities ();
847  if (data.currency_valid)
848  {
849  DEBUG("Currency from libofx: %s", data.currency);
850  default_commodity = gnc_commodity_table_lookup(commodity_table,
851  GNC_COMMODITY_NS_CURRENCY,
852  data.currency);
853  }
854  else
855  {
856  default_commodity = NULL;
857  }
858 
859  if (data.account_type_valid)
860  {
861  switch (data.account_type)
862  {
863  case OFX_CHECKING :
864  default_type = ACCT_TYPE_BANK;
865  account_type_name = _("Unknown OFX checking account");
866  break;
867  case OFX_SAVINGS :
868  default_type = ACCT_TYPE_BANK;
869  account_type_name = _("Unknown OFX savings account");
870  break;
871  case OFX_MONEYMRKT :
872  default_type = ACCT_TYPE_MONEYMRKT;
873  account_type_name = _("Unknown OFX money market account");
874  break;
875  case OFX_CREDITLINE :
876  default_type = ACCT_TYPE_CREDITLINE;
877  account_type_name = _("Unknown OFX credit line account");
878  break;
879  case OFX_CMA :
880  default_type = ACCT_TYPE_NONE;
881  account_type_name = _("Unknown OFX CMA account");
882  break;
883  case OFX_CREDITCARD :
884  default_type = ACCT_TYPE_CREDIT;
885  account_type_name = _("Unknown OFX credit card account");
886  break;
887  case OFX_INVESTMENT :
888  default_type = ACCT_TYPE_BANK;
889  account_type_name = _("Unknown OFX investment account");
890  break;
891  default:
892  PERR("WRITEME: ofx_proc_account() This is an unknown account type!");
893  break;
894  }
895  }
896 
897  /* If the OFX importer was started in Gnucash in a 'new_book' situation,
898  * as described above, the first time the 'ofx_proc_account_cb' function
899  * is called a book is created. (This happens after the 'new_book' flag
900  * is set in 'gnc_get_current_commodities', called above.) So, before
901  * calling 'gnc_import_select_account', allow the user to set book
902  * options. */
903  if (new_book)
904  new_book = gnc_new_book_option_display(gnc_ui_get_toplevel());
905 
906  gnc_utf8_strip_invalid(data.account_name);
907  gnc_utf8_strip_invalid(data.account_id);
908  account_description = g_strdup_printf( /* This string is a default account
909  name. It MUST NOT contain the
910  character ':' anywhere in it or
911  in any translation. */
912  "%s \"%s\"",
913  account_type_name,
914  data.account_name);
915  gnc_import_select_account(NULL, data.account_id, 1,
916  account_description, default_commodity,
917  default_type, NULL, NULL);
918  g_free(account_description);
919  }
920  else
921  {
922  PERR("account online ID not available");
923  }
924 
925  return 0;
926 }
927 
928 double ofx_get_investment_amount(const struct OfxTransactionData* data)
929 {
930  g_assert(data);
931  switch (data->invtransactiontype)
932  {
933  case OFX_BUYDEBT:
934  case OFX_BUYMF:
935  case OFX_BUYOPT:
936  case OFX_BUYOTHER:
937  case OFX_BUYSTOCK:
938  return fabs(data->amount);
939  case OFX_SELLDEBT:
940  case OFX_SELLMF:
941  case OFX_SELLOPT:
942  case OFX_SELLOTHER:
943  case OFX_SELLSTOCK:
944  return -1 * fabs(data->amount);
945  default:
946  return -1 * data->amount;
947  }
948 }
949 
951 {
952  extern int ofx_PARSER_msg;
953  extern int ofx_DEBUG_msg;
954  extern int ofx_WARNING_msg;
955  extern int ofx_ERROR_msg;
956  extern int ofx_INFO_msg;
957  extern int ofx_STATUS_msg;
958  char *selected_filename;
959  char *default_dir;
960  LibofxContextPtr libofx_context = libofx_get_new_context();
961 
962  ofx_PARSER_msg = false;
963  ofx_DEBUG_msg = false;
964  ofx_WARNING_msg = true;
965  ofx_ERROR_msg = true;
966  ofx_INFO_msg = true;
967  ofx_STATUS_msg = false;
968 
969  DEBUG("gnc_file_ofx_import(): Begin...\n");
970 
971  default_dir = gnc_get_default_directory(GNC_PREFS_GROUP);
972  selected_filename = gnc_file_dialog(_("Select an OFX/QFX file to process"),
973  NULL,
974  default_dir,
975  GNC_FILE_DIALOG_IMPORT);
976  g_free(default_dir);
977 
978  if (selected_filename != NULL)
979  {
980 #ifdef G_OS_WIN32
981  gchar *conv_name;
982 #endif
983 
984  /* Remember the directory as the default. */
985  default_dir = g_path_get_dirname(selected_filename);
986  gnc_set_default_directory(GNC_PREFS_GROUP, default_dir);
987  g_free(default_dir);
988 
989  /*strncpy(file,selected_filename, 255);*/
990  DEBUG("Filename found: %s", selected_filename);
991 
992  /* Create the Generic transaction importer GUI. */
993  gnc_ofx_importer_gui = gnc_gen_trans_list_new(NULL, NULL, FALSE, 42);
994 
995  /* Look up the needed preferences */
996  auto_create_commodity =
997  gnc_prefs_get_bool (GNC_PREFS_GROUP_IMPORT, GNC_PREF_AUTO_COMMODITY);
998 
999  /* Initialize libofx */
1000 
1001  /*ofx_set_statement_cb(libofx_context, ofx_proc_statement_cb, 0);*/
1002  ofx_set_account_cb(libofx_context, ofx_proc_account_cb, 0);
1003  ofx_set_transaction_cb(libofx_context, ofx_proc_transaction_cb, 0);
1004  ofx_set_security_cb(libofx_context, ofx_proc_security_cb, 0);
1005  /*ofx_set_status_cb(libofx_context, ofx_proc_status_cb, 0);*/
1006 
1007 #ifdef G_OS_WIN32
1008  conv_name = g_win32_locale_filename_from_utf8(selected_filename);
1009  g_free(selected_filename);
1010  selected_filename = conv_name;
1011 #endif
1012 
1013  DEBUG("Opening selected file");
1014  libofx_proc_file(libofx_context, selected_filename, AUTODETECT);
1015  g_free(selected_filename);
1016  }
1017 
1018  if (ofx_created_commodites)
1019  {
1020  /* FIXME: Present some result window about the newly created
1021  * commodities */
1022  g_warning("Created %d new commodities during import", g_list_length(ofx_created_commodites));
1023  g_list_free(ofx_created_commodites);
1024  ofx_created_commodites = NULL;
1025  }
1026  else
1027  {
1028  //g_warning("No new commodities created");
1029  }
1030 }
1031 
1032 
void xaccSplitSetValue(Split *s, gnc_numeric amt)
Definition: Split.c:1294
gnc_commodity * gnc_commodity_table_insert(gnc_commodity_table *table, gnc_commodity *comm)
#define xaccTransAppendSplit(t, s)
Definition: Transaction.h:357
Transaction * xaccMallocTransaction(QofBook *book)
Definition: Transaction.c:513
void xaccSplitSetBaseValue(Split *s, gnc_numeric value, const gnc_commodity *base_currency)
Definition: Split.c:1341
void xaccTransSetDatePostedSecsNormalized(Transaction *trans, time64 time)
Definition: Transaction.c:1920
gchar * gnc_timespec_to_iso8601_buff(Timespec ts, gchar *buff)
int gnc_commodity_get_fraction(const gnc_commodity *cm)
Dialog for create/edit an account.
gnc_numeric double_to_gnc_numeric(double n, gint64 denom, gint how)
gnc_commodity * gnc_import_select_commodity(const char *cusip, gboolean ask_on_unknown, const char *default_fullname, const char *default_mnemonic)
utility functions for the GnuCash UI
GNCAccountType xaccAccountGetType(const Account *acc)
Definition: Account.c:3009
void xaccTransSetNotes(Transaction *trans, const char *notes)
Definition: Transaction.c:2115
#define DEBUG(format, args...)
Definition: qoflog.h:255
#define GNC_PREFS_GROUP_IMPORT
void xaccTransSetDescription(Transaction *trans, const char *desc)
Definition: Transaction.c:2085
Account * gnc_import_select_account(GtkWidget *parent, const gchar *account_online_id_value, gboolean auto_create, const gchar *account_human_description, const gnc_commodity *new_account_default_commodity, GNCAccountType new_account_default_type, Account *default_selection, gboolean *ok_pressed)
Transaction matcher main window.
Use a 64-bit unsigned int timespec.
Definition: gnc-date.h:299
Ofx import module interface.
Generic and very flexible account matcher/picker.
gnc_quote_source * gnc_quote_source_lookup_by_ti(QuoteSourceType type, gint index)
#define PERR(format, args...)
Definition: qoflog.h:237
GNCImportMainMatcher * gnc_gen_trans_list_new(GtkWidget *parent, const gchar *heading, gboolean all_from_same_account, gint match_date_hardlimit)
void gnc_commodity_user_set_quote_flag(gnc_commodity *cm, const gboolean flag)
void gnc_gen_trans_list_add_trans(GNCImportMainMatcher *gui, Transaction *trans)
void xaccTransSetCurrency(Transaction *trans, gnc_commodity *curr)
Definition: Transaction.c:1354
void xaccTransDestroy(Transaction *trans)
Definition: Transaction.c:1402
int xaccTransCountSplits(const Transaction *trans)
Definition: Transaction.c:2170
void gnc_commodity_set_quote_source(gnc_commodity *cm, gnc_quote_source *src)
void xaccSplitSetAmount(Split *s, gnc_numeric amt)
Definition: Split.c:1258
Account handling public routines.
gboolean xaccAccountTypesCompatible(GNCAccountType parent_type, GNCAccountType child_type)
Definition: Account.c:4194
GtkWidget * gnc_gen_trans_list_widget(GNCImportMainMatcher *info)
void xaccSplitSetMemo(Split *split, const char *memo)
Definition: Split.c:1774
gnc_commodity * gnc_commodity_new(QofBook *book, const char *fullname, const char *name_space, const char *mnemonic, const char *cusip, int fraction)
const char * gnc_commodity_get_fullname(const gnc_commodity *cm)
void gnc_utf8_strip_invalid(gchar *str)
void gnc_file_ofx_import(void)
void xaccTransCommitEdit(Transaction *trans)
Definition: Transaction.c:1579
void xaccTransBeginEdit(Transaction *trans)
Definition: Transaction.c:1380
All type declarations for the whole Gnucash engine.
GNCAccountType
Definition: Account.h:96
Split * xaccMallocSplit(QofBook *book)
Definition: Split.c:582
GLib helper routines.
Generic api to store and retrieve preferences.
gchar * gnc_utf8_strip_invalid_strdup(const gchar *str)
Definition: SplitP.h:71
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Definition: Account.c:3148
gnc_commodity * xaccTransGetCurrency(const Transaction *trans)
Definition: Transaction.c:1348
#define xaccAccountInsertSplit(acc, s)
Definition: Account.h:972
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Definition: gnc-prefs.c:196
Utility functions for writing import modules.
Account * gnc_ui_new_accounts_from_name_with_defaults(const char *name, GList *valid_types, const gnc_commodity *default_commodity, Account *parent)
time64 gnc_time(time64 *tbuf)
get the current local time
GtkWidget * gnc_ui_get_toplevel(void)
gint64 time64
Definition: gnc-date.h:83
void xaccTransSetDateEnteredSecs(Transaction *trans, time64 secs)
Definition: Transaction.c:1951
const char * xaccAccountGetName(const Account *acc)
Definition: Account.c:3031
A Generic commodity matcher/picker.
API for Transactions and Splits (journal entries)
const gchar * QofLogModule
Definition: qofid.h:89
void timespecFromTime64(Timespec *ts, time64 t)