r20413 - gnucash/trunk/src/import-export/ofx - Implement automatic creation of stock account for newly imported OFX stocks.
Christian Stimming
cstim at code.gnucash.org
Sun Mar 13 07:30:49 EDT 2011
Author: cstim
Date: 2011-03-13 07:28:37 -0400 (Sun, 13 Mar 2011)
New Revision: 20413
Trac: http://svn.gnucash.org/trac/changeset/20413
Modified:
gnucash/trunk/src/import-export/ofx/gnc-ofx-import.c
Log:
Implement automatic creation of stock account for newly imported OFX stocks.
Only active if "auto-create commodity" is switched on in the preferences
from r20376.
Modified: gnucash/trunk/src/import-export/ofx/gnc-ofx-import.c
===================================================================
--- gnucash/trunk/src/import-export/ofx/gnc-ofx-import.c 2011-03-13 11:14:22 UTC (rev 20412)
+++ gnucash/trunk/src/import-export/ofx/gnc-ofx-import.c 2011-03-13 11:28:37 UTC (rev 20413)
@@ -48,6 +48,8 @@
#include "gnc-ui-util.h"
#include "gnc-glib-utils.h"
#include "core-utils/gnc-gconf-utils.h"
+#include "gnome-utils/gnc-ui.h"
+#include "gnome-utils/dialog-account.h"
#include "gnc-ofx-kvp.h"
@@ -64,6 +66,7 @@
ofx_proc_transaction_cb can use it. */
GNCImportMainMatcher *gnc_ofx_importer_gui = NULL;
static gboolean auto_create_commodity = FALSE;
+static Account *ofx_parent_account = NULL;
GList *ofx_created_commodites = NULL;
@@ -77,7 +80,7 @@
int ofx_proc_security_cb(const struct OfxSecurityData data, void * security_user_data);
int ofx_proc_transaction_cb(struct OfxTransactionData data, void * transaction_user_data);
int ofx_proc_account_cb(struct OfxAccountData data, void * account_user_data);
-double ofx_get_investment_amount(struct OfxTransactionData data);
+static double ofx_get_investment_amount(const struct OfxTransactionData* data);
static const gchar *gnc_ofx_ttype_to_string(TransactionType t)
{
@@ -271,6 +274,43 @@
return gnc_ofx_numeric_from_double(value, xaccTransGetCurrency(txn));
}
+/* Opens the dialog to create a new account with given name, commodity, parent, type.
+ * Returns the new account, or NULL if it couldn't be created.. */
+static Account *gnc_ofx_new_account(const char* name,
+ const gnc_commodity * account_commodity,
+ Account *parent_account,
+ GNCAccountType new_account_default_type)
+{
+ Account *result;
+ GList * valid_types = NULL;
+
+ g_assert(name);
+ g_assert(account_commodity);
+ g_assert(parent_account);
+
+ if (new_account_default_type != ACCT_TYPE_NONE)
+ {
+ // Passing the types as gpointer
+ valid_types =
+ g_list_prepend(valid_types,
+ GINT_TO_POINTER(new_account_default_type));
+ if (!xaccAccountTypesCompatible(xaccAccountGetType(parent_account), new_account_default_type))
+ {
+ // Need to add the parent's account type
+ valid_types =
+ g_list_prepend(valid_types,
+ GINT_TO_POINTER(xaccAccountGetType(parent_account)));
+ }
+ }
+ result = gnc_ui_new_accounts_from_name_with_defaults (name,
+ valid_types,
+ account_commodity,
+ parent_account);
+ g_list_free(valid_types);
+ return result;
+}
+
+
int ofx_proc_transaction_cb(struct OfxTransactionData data, void * transaction_user_data)
{
char dest_string[255];
@@ -295,7 +335,8 @@
return 0;
}
- account = gnc_import_select_account(NULL, data.account_id, 0, NULL, NULL,
+ account = gnc_import_select_account(gnc_gen_trans_list_widget(gnc_ofx_importer_gui),
+ data.account_id, 0, NULL, NULL,
ACCT_TYPE_NONE, NULL, NULL);
if (account == NULL)
{
@@ -456,11 +497,13 @@
gnc_import_set_split_online_id(split, data.fi_id);
}
}
+
else if (data.unique_id_valid
&& data.security_data_valid
&& data.security_data_ptr != NULL
&& data.security_data_ptr->secname_valid)
{
+ gboolean choosing_account = TRUE;
/********* Process an investment transaction **********/
/* Note that the ACCT_TYPE_STOCK account type
should be replaced with something derived from
@@ -474,7 +517,6 @@
if (investment_commodity != NULL)
{
// As we now have the commodity, select the account with that commodity.
- // @FIXME: Add the automated selection or creation of account here!
investment_account_text = g_strdup_printf( /* This string is a default account
name. It MUST NOT contain the
@@ -482,20 +524,99 @@
in any translations. */
_("Stock account for security \"%s\""),
data.security_data_ptr->secname);
+
+ // @FIXME: Add the automated selection or creation of account here!
+
+ // First check whether we can find the right investment_account without asking the user
investment_account = gnc_import_select_account(NULL,
- data.unique_id,
- TRUE,
- investment_account_text,
- investment_commodity,
- ACCT_TYPE_STOCK,
- NULL,
- NULL);
+ data.unique_id, FALSE, investment_account_text,
+ investment_commodity, ACCT_TYPE_STOCK, NULL, NULL);
+ // but use it only if that's really the right commodity
+ if (investment_account
+ && xaccAccountGetCommodity(investment_account) != investment_commodity)
+ investment_account = NULL;
+
+ // Loop until we either have an account, or the user pressed Cancel
+ while (!investment_account && choosing_account)
+ {
+ // No account with correct commodity automatically found.
+
+ // But are we in auto-create mode and already know a parent?
+ if (auto_create_commodity && ofx_parent_account)
+ {
+ // Yes, so use that as parent when auto-creating the new account below.
+ investment_account = ofx_parent_account;
+ }
+ else
+ {
+ // Let the user choose an account
+ investment_account = gnc_import_select_account(
+ gnc_gen_trans_list_widget(gnc_ofx_importer_gui),
+ data.unique_id,
+ TRUE,
+ investment_account_text,
+ investment_commodity,
+ ACCT_TYPE_STOCK,
+ NULL,
+ &choosing_account);
+ }
+ // Does the chosen account have the right commodity?
+ if (investment_account && xaccAccountGetCommodity(investment_account) != investment_commodity)
+ {
+ if (auto_create_commodity
+ && xaccAccountTypesCompatible(xaccAccountGetType(investment_account),
+ ACCT_TYPE_STOCK))
+ {
+ // The user chose an account, but it does
+ // not have the right commodity. Also,
+ // auto-creation is on. Hence, we create a
+ // new child account of the selected one,
+ // and this one will have the right
+ // commodity.
+ Account *parent_account = investment_account;
+ investment_account =
+ gnc_ofx_new_account(investment_account_text,
+ investment_commodity,
+ parent_account,
+ ACCT_TYPE_STOCK);
+ if (investment_account)
+ {
+ gnc_import_set_acc_online_id(investment_account, data.unique_id);
+ choosing_account = FALSE;
+ ofx_parent_account = parent_account;
+ }
+ else
+ {
+ ofx_parent_account = NULL;
+ }
+ }
+ else
+ {
+ // No account with matching commodity. Ask the user
+ // whether to continue or abort.
+ choosing_account =
+ gnc_verify_dialog(
+ gnc_gen_trans_list_widget(gnc_ofx_importer_gui), TRUE,
+ "The chosen account \"%s\" does not have the correct "
+ "currency/security \"%s\" (it has \"%s\" instead). "
+ "This account cannot be used. "
+ "Do you want to choose again?",
+ xaccAccountGetName(investment_account),
+ gnc_commodity_get_fullname(investment_commodity),
+ gnc_commodity_get_fullname(xaccAccountGetCommodity(investment_account)));
+ // We must also delete the online_id that was set in gnc_import_select_account()
+ gnc_import_set_acc_online_id(investment_account, "");
+ investment_account = NULL;
+ }
+ }
+ }
if (!investment_account)
{
PERR("No investment account found for text: %s\n", investment_account_text);
}
g_free (investment_account_text);
investment_account_text = NULL;
+
if (investment_account != NULL &&
data.unitprice_valid &&
data.units_valid &&
@@ -506,7 +627,7 @@
xaccTransAppendSplit(transaction, split);
xaccAccountInsertSplit(investment_account, split);
- gnc_amount = gnc_ofx_numeric_from_double (ofx_get_investment_amount(data),
+ gnc_amount = gnc_ofx_numeric_from_double (ofx_get_investment_amount(&data),
investment_commodity);
gnc_units = gnc_ofx_numeric_from_double (data.units, investment_commodity);
xaccSplitSetAmount(split, gnc_units);
@@ -550,14 +671,15 @@
in any translations. */
_("Income account for security \"%s\""),
data.security_data_ptr->secname);
- income_account = gnc_import_select_account(NULL,
- NULL,
- 1,
- investment_account_text,
- currency,
- ACCT_TYPE_INCOME,
- NULL,
- NULL);
+ income_account = gnc_import_select_account(
+ gnc_gen_trans_list_widget(gnc_ofx_importer_gui),
+ NULL,
+ 1,
+ investment_account_text,
+ currency,
+ ACCT_TYPE_INCOME,
+ NULL,
+ NULL);
gnc_ofx_kvp_set_assoc_account(investment_account,
income_account);
DEBUG("KVP written");
@@ -597,28 +719,38 @@
// Set split memo from ofx transaction name or memo
gnc_ofx_set_split_memo(&data, split);
}
+ }
+ if (data.invtransactiontype_valid
+ && data.invtransactiontype != OFX_REINVEST)
+ {
+ DEBUG("Adding investment split; Money flows from or to the cash account");
+ split = xaccMallocSplit(book);
+ xaccTransAppendSplit(transaction, split);
+ xaccAccountInsertSplit(account, split);
- if (data.invtransactiontype != OFX_REINVEST)
- {
- DEBUG("Adding investment split; Money flows from or to the cash account");
- split = xaccMallocSplit(book);
- xaccTransAppendSplit(transaction, split);
- xaccAccountInsertSplit(account, split);
+ gnc_amount = gnc_ofx_numeric_from_double_txn(
+ -ofx_get_investment_amount(&data), transaction);
+ xaccSplitSetBaseValue(split, gnc_amount,
+ xaccTransGetCurrency(transaction));
- gnc_amount = gnc_ofx_numeric_from_double_txn (-ofx_get_investment_amount(data),
- transaction);
- xaccSplitSetBaseValue(split, gnc_amount, xaccTransGetCurrency(transaction));
-
- // Set split memo from ofx transaction name or memo
- gnc_ofx_set_split_memo(&data, split);
- }
+ // Set split memo from ofx transaction name or memo
+ gnc_ofx_set_split_memo(&data, split);
}
}
- /* Use new importer GUI. */
- DEBUG("%d splits sent to the importer gui", xaccTransCountSplits(transaction));
- gnc_gen_trans_list_add_trans (gnc_ofx_importer_gui, transaction);
+ /* Send transaction to importer GUI. */
+ if (xaccTransCountSplits(transaction) > 0)
+ {
+ DEBUG("%d splits sent to the importer gui", xaccTransCountSplits(transaction));
+ gnc_gen_trans_list_add_trans (gnc_ofx_importer_gui, transaction);
+ }
+ else
+ {
+ PERR("No splits in transaction (missing account?), ignoring.");
+ xaccTransDestroy(transaction);
+ xaccTransCommitEdit(transaction);
+ }
}
else
{
@@ -720,24 +852,25 @@
return 0;
}
-double ofx_get_investment_amount(struct OfxTransactionData data)
+double ofx_get_investment_amount(const struct OfxTransactionData* data)
{
- switch (data.invtransactiontype)
+ g_assert(data);
+ switch (data->invtransactiontype)
{
case OFX_BUYDEBT:
case OFX_BUYMF:
case OFX_BUYOPT:
case OFX_BUYOTHER:
case OFX_BUYSTOCK:
- return fabs(data.amount);
+ return fabs(data->amount);
case OFX_SELLDEBT:
case OFX_SELLMF:
case OFX_SELLOPT:
case OFX_SELLOTHER:
case OFX_SELLSTOCK:
- return -1 * fabs(data.amount);
+ return -1 * fabs(data->amount);
default:
- return -1 * data.amount;
+ return -1 * data->amount;
}
}
More information about the gnucash-changes
mailing list