29 #include <glib/gi18n.h>
35 #include <libofx/libofx.h>
43 #include "engine-helpers.h"
50 #include "gnome-utils/gnc-ui.h"
52 #include "dialog-utils.h"
54 #include "gnc-ofx-kvp.h"
56 #define GNC_PREFS_GROUP "dialogs.import.ofx"
57 #define GNC_PREF_AUTO_COMMODITY "auto-create-commodity"
69 static gboolean auto_create_commodity = FALSE;
70 static Account *ofx_parent_account = NULL;
72 GList *ofx_created_commodites = NULL;
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);
86 static const gchar *gnc_ofx_ttype_to_string(TransactionType t)
91 return "Generic credit";
93 return "Generic debit";
95 return "Interest earned or paid (Note: Depends on signage of amount)";
101 return "Service charge";
105 return "ATM debit or credit (Note: Depends on signage of amount)";
107 return "Point of sale debit or credit (Note: Depends on signage of amount)";
113 return "Electronic payment";
115 return "Cash withdrawal";
117 return "Direct deposit";
118 case OFX_DIRECTDEBIT:
119 return "Merchant initiated debit";
121 return "Repeating payment/standing order";
125 return "Unknown transaction type";
129 static const gchar *gnc_ofx_invttype_to_str(InvTransactionType t)
134 return "BUYDEBT (Buy debt security)";
136 return "BUYMF (Buy mutual fund)";
138 return "BUYOPT (Buy option)";
140 return "BUYOTHER (Buy other security type)";
142 return "BUYSTOCK (Buy stock))";
144 return "CLOSUREOPT (Close a position for an option)";
146 return "INCOME (Investment income is realized as cash into the investment account)";
148 return "INVEXPENSE (Misc investment expense that is associated with a specific security)";
150 return "JRNLFUND (Journaling cash holdings between subaccounts within the same investment account)";
151 case OFX_MARGININTEREST:
152 return "MARGININTEREST (Margin interest expense)";
154 return "REINVEST (Reinvestment of income)";
156 return "RETOFCAP (Return of capital)";
158 return "SELLDEBT (Sell debt security. Used when debt is sold, called, or reached maturity)";
160 return "SELLMF (Sell mutual fund)";
162 return "SELLOPT (Sell option)";
164 return "SELLOTHER (Sell other type of security)";
166 return "SELLSTOCK (Sell stock)";
168 return "SPLIT (Stock or mutial fund split)";
170 return "TRANSFER (Transfer holdings in and out of the investment account)";
172 return "ERROR, this investment transaction type is unknown. This is a bug in ofxdump";
178 sanitize_string (gchar* str)
181 const int length = -1;
182 while (!g_utf8_validate (str, length, (
const gchar **)(&inval)))
187 int ofx_proc_security_cb(
const struct OfxSecurityData data,
void * security_user_data)
190 char* default_fullname = NULL;
191 char* default_mnemonic = NULL;
193 if (data.unique_id_valid)
197 if (data.secname_valid)
201 if (data.ticker_valid)
206 if (auto_create_commodity)
216 QofBook *book = gnc_get_current_book();
218 gint source_selection = 0;
219 char *commodity_namespace = NULL;
222 if (data.unique_id_type_valid)
227 g_warning(
"Creating a new commodity, cusip=%s", cusip);
237 gnc_commodity_begin_edit(commodity);
241 gnc_commodity_commit_edit(commodity);
247 ofx_created_commodites = g_list_prepend(ofx_created_commodites, commodity);
248 g_free (commodity_namespace);
261 g_free (default_mnemonic);
262 g_free (default_fullname);
266 static void gnc_ofx_set_split_memo(
const struct OfxTransactionData* data,
Split *split)
273 if (data->name_valid)
277 else if (data->memo_valid)
295 static Account *gnc_ofx_new_account(
const char* name,
301 GList * valid_types = NULL;
304 g_assert(account_commodity);
305 g_assert(parent_account);
311 g_list_prepend(valid_types,
312 GINT_TO_POINTER(new_account_default_type));
317 g_list_prepend(valid_types,
325 g_list_free(valid_types);
330 int ofx_proc_transaction_cb(
struct OfxTransactionData data,
void * transaction_user_data)
332 char dest_string[255];
335 Account *investment_account = NULL;
336 Account *income_account = NULL;
337 gchar *investment_account_text, *investment_account_onlineid;
346 g_assert(gnc_ofx_importer_gui);
348 if (!data.account_id_valid)
350 PERR(
"account ID for this transaction is unavailable!");
362 PERR(
"Unable to find account for id %s", data.account_id);
370 if (data.check_number_valid)
372 if (data.reference_number_valid)
376 book = gnc_account_get_book(account);
386 if (data.date_posted_valid && (data.date_posted != 0))
391 else if (data.date_initiated_valid && (data.date_initiated != 0))
409 else if (data.memo_valid)
415 notes = g_strdup_printf(
"OFX ext. info: ");
417 if (data.transactiontype_valid)
420 notes = g_strdup_printf(
"%s%s%s", tmp,
"|Trans type:",
421 gnc_ofx_ttype_to_string(data.transactiontype));
425 if (data.invtransactiontype_valid)
428 notes = g_strdup_printf(
"%s%s%s", tmp,
"|Investment Trans type:",
429 gnc_ofx_invttype_to_str(data.invtransactiontype));
432 if (data.memo_valid && data.name_valid)
435 notes = g_strdup_printf(
"%s%s%s", tmp,
"|Memo:", data.memo);
438 if (data.date_funds_available_valid)
444 notes = g_strdup_printf(
"%s%s%s", tmp,
445 "|Date funds available:", dest_string);
448 if (data.server_transaction_id_valid)
451 notes = g_strdup_printf(
"%s%s%s", tmp,
452 "|Server trans ID (conf. number):",
453 sanitize_string (data.server_transaction_id));
456 if (data.standard_industrial_code_valid)
459 notes = g_strdup_printf(
"%s%s%ld", tmp,
460 "|Standard Industrial Code:",
461 data.standard_industrial_code);
465 if (data.payee_id_valid)
468 notes = g_strdup_printf(
"%s%s%s", tmp,
"|Payee ID:",
469 sanitize_string (data.payee_id));
476 if (data.fi_id_corrected_valid)
478 PERR(
"WRITEME: GnuCash ofx_proc_transaction(): WARNING: This transaction corrected a previous transaction, but we created a new one instead!\n");
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!");
489 if (data.account_ptr && data.account_ptr->currency_valid)
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);
498 DEBUG(
"Currency from libofx unavailable, defaulting to account's default");
503 if (data.amount_valid)
505 if (!data.invtransactiontype_valid)
508 DEBUG(
"Adding split; Ordinary banking transaction, money flows from or into the source account");
513 gnc_amount = gnc_ofx_numeric_from_double_txn(data.amount, transaction);
517 if (data.check_number_valid)
519 gnc_set_num_action(transaction, split, data.check_number, NULL);
521 else if (data.reference_number_valid)
523 gnc_set_num_action(transaction, split, data.reference_number, NULL);
531 if (data.fi_id_valid)
533 gnc_import_set_split_online_id(split,
534 sanitize_string (data.fi_id));
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)
543 gboolean choosing_account = TRUE;
555 if (investment_commodity != NULL)
559 investment_account_text = g_strdup_printf(
563 _(
"Stock account for security \"%s\""),
564 sanitize_string (data.security_data_ptr->secname));
566 investment_account_onlineid = g_strdup_printf(
"%s%s",
570 investment_account_onlineid,
572 investment_account_text,
573 investment_commodity,
579 if (investment_account
581 investment_account = NULL;
584 while (!investment_account && choosing_account)
589 if (auto_create_commodity && ofx_parent_account)
592 investment_account = ofx_parent_account;
601 investment_account_text,
602 investment_commodity,
610 if (auto_create_commodity
620 Account *parent_account = investment_account;
622 gnc_ofx_new_account(investment_account_text,
623 investment_commodity,
626 if (investment_account)
628 gnc_import_set_acc_online_id(investment_account, data.unique_id);
629 choosing_account = FALSE;
630 ofx_parent_account = parent_account;
634 ofx_parent_account = NULL;
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?",
652 gnc_import_set_acc_online_id(investment_account,
"");
653 investment_account = NULL;
657 if (!investment_account)
659 PERR(
"No investment account found for text: %s\n", investment_account_text);
661 g_free (investment_account_text);
662 g_free (investment_account_onlineid);
663 investment_account_text = NULL;
665 if (investment_account != NULL &&
666 data.unitprice_valid &&
668 ( data.invtransactiontype != OFX_INCOME ) )
670 DEBUG(
"Adding investment split; Money flows from or into the stock account");
675 gnc_amount = gnc_ofx_numeric_from_double_txn (ofx_get_investment_amount(&data),
677 gnc_units = gnc_ofx_numeric_from_double (data.units, investment_commodity);
682 if (data.check_number_valid)
684 gnc_set_num_action(transaction, split, data.check_number, NULL);
686 else if (data.reference_number_valid)
688 gnc_set_num_action(transaction, split,
689 data.reference_number, NULL);
691 if (data.security_data_ptr->memo_valid)
694 sanitize_string (data.security_data_ptr->memo));
696 if (data.fi_id_valid)
698 gnc_import_set_split_online_id(split,
699 sanitize_string (data.fi_id));
704 if (investment_account)
705 PERR(
"The investment account, units or unitprice was not found for the investment transaction");
710 PERR(
"Commodity not found for the investment transaction");
713 if (data.invtransactiontype_valid && investment_account)
715 if (data.invtransactiontype == OFX_REINVEST
716 || data.invtransactiontype == OFX_INCOME)
718 DEBUG(
"Now let's find an account for the destination split");
720 income_account = gnc_ofx_kvp_get_assoc_account(investment_account);
722 if (income_account == NULL)
724 DEBUG(
"Couldn't find an associated income account");
725 investment_account_text = g_strdup_printf(
729 _(
"Income account for security \"%s\""),
730 sanitize_string (data.security_data_ptr->secname));
735 investment_account_text,
740 if (income_account != NULL)
742 gnc_ofx_kvp_set_assoc_account(investment_account,
744 DEBUG(
"KVP written");
750 DEBUG(
"Found at least one associated income account");
753 if (income_account != NULL &&
754 data.invtransactiontype == OFX_REINVEST)
756 DEBUG(
"Adding investment split; Money flows from the income account");
761 gnc_amount = gnc_ofx_numeric_from_double_txn (data.amount, transaction);
765 gnc_ofx_set_split_memo(&data, split);
767 if (income_account != NULL &&
768 data.invtransactiontype == OFX_INCOME)
770 DEBUG(
"Adding investment split; Money flows from the income account");
775 gnc_amount = gnc_ofx_numeric_from_double_txn (-data.amount,
780 gnc_ofx_set_split_memo(&data, split);
784 if (data.invtransactiontype_valid
785 && data.invtransactiontype != OFX_REINVEST)
787 DEBUG(
"Adding investment split; Money flows from or to the cash account");
792 gnc_amount = gnc_ofx_numeric_from_double_txn(
793 -ofx_get_investment_amount(&data), transaction);
798 gnc_ofx_set_split_memo(&data, split);
810 PERR(
"No splits in transaction (missing account?), ignoring.");
817 PERR(
"The transaction doesn't have a valid amount");
832 int ofx_proc_account_cb(
struct OfxAccountData data,
void * account_user_data)
837 gchar * account_description;
840 gboolean new_book = gnc_is_new_book();
842 const gchar * account_type_name = _(
"Unknown OFX account");
844 if (data.account_id_valid)
846 commodity_table = gnc_get_current_commodities ();
847 if (data.currency_valid)
849 DEBUG(
"Currency from libofx: %s", data.currency);
850 default_commodity = gnc_commodity_table_lookup(commodity_table,
851 GNC_COMMODITY_NS_CURRENCY,
856 default_commodity = NULL;
859 if (data.account_type_valid)
861 switch (data.account_type)
865 account_type_name = _(
"Unknown OFX checking account");
869 account_type_name = _(
"Unknown OFX savings account");
873 account_type_name = _(
"Unknown OFX money market account");
875 case OFX_CREDITLINE :
877 account_type_name = _(
"Unknown OFX credit line account");
881 account_type_name = _(
"Unknown OFX CMA account");
883 case OFX_CREDITCARD :
885 account_type_name = _(
"Unknown OFX credit card account");
887 case OFX_INVESTMENT :
889 account_type_name = _(
"Unknown OFX investment account");
892 PERR(
"WRITEME: ofx_proc_account() This is an unknown account type!");
908 account_description = g_strdup_printf(
916 account_description, default_commodity,
917 default_type, NULL, NULL);
918 g_free(account_description);
922 PERR(
"account online ID not available");
928 double ofx_get_investment_amount(
const struct OfxTransactionData* data)
931 switch (data->invtransactiontype)
938 return fabs(data->amount);
944 return -1 * fabs(data->amount);
946 return -1 * data->amount;
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;
960 LibofxContextPtr libofx_context = libofx_get_new_context();
962 ofx_PARSER_msg =
false;
963 ofx_DEBUG_msg =
false;
964 ofx_WARNING_msg =
true;
965 ofx_ERROR_msg =
true;
967 ofx_STATUS_msg =
false;
969 DEBUG(
"gnc_file_ofx_import(): Begin...\n");
971 default_dir = gnc_get_default_directory(GNC_PREFS_GROUP);
972 selected_filename = gnc_file_dialog(_(
"Select an OFX/QFX file to process"),
975 GNC_FILE_DIALOG_IMPORT);
978 if (selected_filename != NULL)
985 default_dir = g_path_get_dirname(selected_filename);
986 gnc_set_default_directory(GNC_PREFS_GROUP, default_dir);
990 DEBUG(
"Filename found: %s", selected_filename);
996 auto_create_commodity =
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);
1008 conv_name = g_win32_locale_filename_from_utf8(selected_filename);
1009 g_free(selected_filename);
1010 selected_filename = conv_name;
1013 DEBUG(
"Opening selected file");
1014 libofx_proc_file(libofx_context, selected_filename, AUTODETECT);
1015 g_free(selected_filename);
1018 if (ofx_created_commodites)
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;
void xaccSplitSetValue(Split *s, gnc_numeric amt)
gnc_commodity * gnc_commodity_table_insert(gnc_commodity_table *table, gnc_commodity *comm)
#define xaccTransAppendSplit(t, s)
Transaction * xaccMallocTransaction(QofBook *book)
void xaccSplitSetBaseValue(Split *s, gnc_numeric value, const gnc_commodity *base_currency)
void xaccTransSetDatePostedSecsNormalized(Transaction *trans, time64 time)
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)
void xaccTransSetNotes(Transaction *trans, const char *notes)
#define DEBUG(format, args...)
#define GNC_PREFS_GROUP_IMPORT
void xaccTransSetDescription(Transaction *trans, const char *desc)
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.
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...)
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)
void xaccTransDestroy(Transaction *trans)
int xaccTransCountSplits(const Transaction *trans)
void gnc_commodity_set_quote_source(gnc_commodity *cm, gnc_quote_source *src)
void xaccSplitSetAmount(Split *s, gnc_numeric amt)
Account handling public routines.
gboolean xaccAccountTypesCompatible(GNCAccountType parent_type, GNCAccountType child_type)
GtkWidget * gnc_gen_trans_list_widget(GNCImportMainMatcher *info)
void xaccSplitSetMemo(Split *split, const char *memo)
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)
void xaccTransBeginEdit(Transaction *trans)
All type declarations for the whole Gnucash engine.
Split * xaccMallocSplit(QofBook *book)
Generic api to store and retrieve preferences.
gchar * gnc_utf8_strip_invalid_strdup(const gchar *str)
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
gnc_commodity * xaccTransGetCurrency(const Transaction *trans)
#define xaccAccountInsertSplit(acc, s)
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
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)
void xaccTransSetDateEnteredSecs(Transaction *trans, time64 secs)
const char * xaccAccountGetName(const Account *acc)
A Generic commodity matcher/picker.
API for Transactions and Splits (journal entries)
const gchar * QofLogModule
void timespecFromTime64(Timespec *ts, time64 t)