gnucash maint: Multiple changes pushed
John Ralls
jralls at code.gnucash.org
Tue Dec 13 16:15:55 EST 2022
Updated via https://github.com/Gnucash/gnucash/commit/73a13473 (commit)
via https://github.com/Gnucash/gnucash/commit/598a6f1b (commit)
via https://github.com/Gnucash/gnucash/commit/f6cc6eda (commit)
via https://github.com/Gnucash/gnucash/commit/5e7bc1d6 (commit)
via https://github.com/Gnucash/gnucash/commit/86284838 (commit)
via https://github.com/Gnucash/gnucash/commit/6a9e1cb2 (commit)
via https://github.com/Gnucash/gnucash/commit/17eb739d (commit)
via https://github.com/Gnucash/gnucash/commit/0bc2d692 (commit)
via https://github.com/Gnucash/gnucash/commit/5519a9d7 (commit)
via https://github.com/Gnucash/gnucash/commit/e116a4f1 (commit)
from https://github.com/Gnucash/gnucash/commit/b4bab92d (commit)
commit 73a134730f682e54e15f4813df6946abdb45db4a
Author: John Ralls <jralls at ceridwen.us>
Date: Tue Dec 13 13:13:13 2022 -0800
Bug 798680 - Not able to match a reverse transaction of a...
previously matched transaction.
Cloned transactions and copied splits shouldn't keep the online-id
slot.
diff --git a/libgnucash/engine/Split.c b/libgnucash/engine/Split.c
index d8fc1b784..af9588982 100644
--- a/libgnucash/engine/Split.c
+++ b/libgnucash/engine/Split.c
@@ -624,6 +624,8 @@ void
xaccSplitCopyKvp (const Split *from, Split *to)
{
qof_instance_copy_kvp (QOF_INSTANCE (to), QOF_INSTANCE (from));
+ /* But not the online-id */
+ qof_instance_set (QOF_INSTANCE (to), "online-id", NULL, NULL);
}
/*################## Added for Reg2 #################*/
diff --git a/libgnucash/engine/Transaction.c b/libgnucash/engine/Transaction.c
index ce4761b6b..d8255f14c 100644
--- a/libgnucash/engine/Transaction.c
+++ b/libgnucash/engine/Transaction.c
@@ -693,6 +693,9 @@ xaccTransClone (const Transaction *from)
xaccTransBeginEdit (to);
qof_instance_copy_kvp (QOF_INSTANCE (to), QOF_INSTANCE (from));
+ /* But not the online-id! */
+ qof_instance_set (QOF_INSTANCE (to), "online-id", NULL, NULL);
+
for (GList* lfrom = from->splits, *lto = to->splits; lfrom && lto;
lfrom = g_list_next (lfrom), lto = g_list_next (lto))
xaccSplitCopyKvp (lfrom->data, lto->data);
commit 598a6f1b6b565e9eb335c43c278202d498ff1fc3
Author: John Ralls <jralls at ceridwen.us>
Date: Tue Dec 13 12:03:03 2022 -0800
[ofx import] Propose parent account unless last account is right commodity.
diff --git a/gnucash/import-export/ofx/gnc-ofx-import.c b/gnucash/import-export/ofx/gnc-ofx-import.c
index 542ed4f6c..516b5e209 100644
--- a/gnucash/import-export/ofx/gnc-ofx-import.c
+++ b/gnucash/import-export/ofx/gnc-ofx-import.c
@@ -649,13 +649,19 @@ static Account*
choose_investment_account_helper(OfxTransactionData *data, ofx_info *info,
InvestmentAcctData *inv_data)
{
- Account *investment_account =
+ Account *investment_account, *parent_account;
+
+ if (xaccAccountGetCommodity(info->last_investment_account) == inv_data->commodity)
+ parent_account = info->last_investment_account;
+ else
+ parent_account = ofx_parent_account;
+
+ investment_account =
gnc_import_select_account(GTK_WIDGET(info->parent),
inv_data->online_id,
TRUE, inv_data->acct_text,
inv_data->commodity, ACCT_TYPE_STOCK,
- info->last_investment_account,
- &inv_data->choosing);
+ parent_account, &inv_data->choosing);
if (investment_account &&
xaccAccountGetCommodity(investment_account) == inv_data->commodity)
{
commit f6cc6eda4c11a204c9abe4e7e6d681fb019fe00e
Author: John Ralls <jralls at ceridwen.us>
Date: Tue Dec 13 11:53:42 2022 -0800
[ofx import] Make automatic account creation work.
When Preferences>Import>Automatic commodity creation is enabled.
Note that this behavior isn't indicated in the tooltip and is a bit
clunky: the user has to cancel the manual account selection for it
to fire.
diff --git a/gnucash/import-export/ofx/gnc-ofx-import.c b/gnucash/import-export/ofx/gnc-ofx-import.c
index 453213450..542ed4f6c 100644
--- a/gnucash/import-export/ofx/gnc-ofx-import.c
+++ b/gnucash/import-export/ofx/gnc-ofx-import.c
@@ -649,47 +649,34 @@ static Account*
choose_investment_account_helper(OfxTransactionData *data, ofx_info *info,
InvestmentAcctData *inv_data)
{
- Account *investment_account = NULL;
- // 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(GTK_WIDGET(info->parent), data->unique_id,
- TRUE, inv_data->acct_text,
- inv_data->commodity, ACCT_TYPE_STOCK,
- info->last_investment_account,
- &inv_data->choosing);
- if (investment_account)
- info->last_investment_account = investment_account;
- }
- // Does the chosen account have the right commodity?
+ Account *investment_account =
+ gnc_import_select_account(GTK_WIDGET(info->parent),
+ inv_data->online_id,
+ TRUE, inv_data->acct_text,
+ inv_data->commodity, ACCT_TYPE_STOCK,
+ info->last_investment_account,
+ &inv_data->choosing);
if (investment_account &&
xaccAccountGetCommodity(investment_account) == inv_data->commodity)
- return investment_account;
+ {
+ Account *parent_account = gnc_account_get_parent(investment_account);
+
+ if (!ofx_parent_account && parent_account &&
+ !gnc_account_is_root(parent_account) &&
+ xaccAccountTypesCompatible(xaccAccountGetType(parent_account),
+ ACCT_TYPE_STOCK))
+ ofx_parent_account = parent_account;
- if (!investment_account) // Nothing more to try
- return NULL;
+ info->last_investment_account = investment_account;
+ return investment_account;
+ }
- if (auto_create_commodity &&
- xaccAccountTypesCompatible(xaccAccountGetType(investment_account),
- ACCT_TYPE_STOCK))
+ /* That didn't work out. Create a subaccount if we can. */
+ if (auto_create_commodity && ofx_parent_account)
{
- /* We have an account but the commodity doesn't match. Since
- * auto_create_commodity is on and the account has a
- * compatible type we can just create a sub-account with the
- * right commodity.
- */
investment_account =
create_investment_subaccount(GTK_WINDOW(info->parent),
- investment_account,
+ ofx_parent_account,
inv_data);
}
else
@@ -711,8 +698,8 @@ choose_investment_account(OfxTransactionData *data, ofx_info *info,
{
Account* investment_account = NULL;
gnc_numeric gnc_amount, gnc_units;
- InvestmentAcctData inv_data = {commodity, NULL, NULL, FALSE};
- char *acct_online_id;
+ InvestmentAcctData inv_data = {commodity, NULL, NULL, TRUE};
+
// As we now have the commodity, select the account with that commodity.
/* Translators: This string is a default account name. It MUST
@@ -721,21 +708,9 @@ choose_investment_account(OfxTransactionData *data, ofx_info *info,
inv_data.acct_text = g_strdup_printf(
_("Stock account for security \"%s\""),
sanitize_string (data->security_data_ptr->secname));
- inv_data.online_id = data->unique_id;
-
- acct_online_id = g_strdup_printf("%s%s", data->account_id, data->unique_id);
- investment_account =
- gnc_import_select_account(GTK_WIDGET(info->parent), acct_online_id,
- TRUE, inv_data.acct_text,
- inv_data.commodity, ACCT_TYPE_STOCK,
- info->last_investment_account, NULL);
- if (investment_account)
- info->last_investment_account = investment_account;
-
- // but use it only if that's really the right commodity
- if (investment_account
- && xaccAccountGetCommodity(investment_account) != inv_data.commodity)
- investment_account = NULL;
+
+ inv_data.online_id =
+ g_strdup_printf("%s%s", data->account_id, data->unique_id);
// Loop until we either have an account, or the user pressed Cancel
while (!investment_account && inv_data.choosing)
@@ -746,7 +721,7 @@ choose_investment_account(OfxTransactionData *data, ofx_info *info,
PERR("No investment account found for text: %s\n", inv_data.acct_text);
}
g_free (inv_data.acct_text);
- g_free (acct_online_id);
+ g_free (inv_data.online_id);
return investment_account;
}
commit 5e7bc1d6c214d1faeab271215721e4348f7a9ef5
Author: John Ralls <jralls at ceridwen.us>
Date: Mon Dec 12 18:02:42 2022 -0800
[ofx import] Fix the parentage of the select account windows.
The account list window is gone when these are used, use the importer parent.
diff --git a/gnucash/import-export/ofx/gnc-ofx-import.c b/gnucash/import-export/ofx/gnc-ofx-import.c
index c58463009..453213450 100644
--- a/gnucash/import-export/ofx/gnc-ofx-import.c
+++ b/gnucash/import-export/ofx/gnc-ofx-import.c
@@ -627,12 +627,12 @@ create_investment_subaccount(GtkWindow *parent, Account* parent_acct,
}
static gboolean
-continue_account_selection(GNCImportMainMatcher* matcher, Account* account,
+continue_account_selection(GtkWidget* parent, Account* account,
gnc_commodity* commodity)
{
gboolean keep_going =
gnc_verify_dialog(
- GTK_WINDOW (gnc_gen_trans_list_widget(matcher)), TRUE,
+ GTK_WINDOW (parent), TRUE,
"The chosen account \"%s\" does not have the correct "
"currency/security \"%s\" (it has \"%s\" instead). "
"This account cannot be used. "
@@ -661,11 +661,12 @@ choose_investment_account_helper(OfxTransactionData *data, ofx_info *info,
else
{
// Let the user choose an account
- investment_account = gnc_import_select_account(
- gnc_gen_trans_list_widget(info->gnc_ofx_importer_gui),
- data->unique_id, TRUE, inv_data->acct_text,
- inv_data->commodity, ACCT_TYPE_STOCK,
- info->last_investment_account, &inv_data->choosing);
+ investment_account =
+ gnc_import_select_account(GTK_WIDGET(info->parent), data->unique_id,
+ TRUE, inv_data->acct_text,
+ inv_data->commodity, ACCT_TYPE_STOCK,
+ info->last_investment_account,
+ &inv_data->choosing);
if (investment_account)
info->last_investment_account = investment_account;
}
@@ -696,9 +697,8 @@ choose_investment_account_helper(OfxTransactionData *data, ofx_info *info,
// No account with matching commodity. Ask the user
// whether to continue or abort.
inv_data->choosing =
- continue_account_selection(info->gnc_ofx_importer_gui,
- investment_account,
- inv_data->commodity);
+ continue_account_selection(GTK_WIDGET(info->parent),
+ investment_account, inv_data->commodity);
investment_account = NULL;
}
@@ -724,10 +724,11 @@ choose_investment_account(OfxTransactionData *data, ofx_info *info,
inv_data.online_id = data->unique_id;
acct_online_id = g_strdup_printf("%s%s", data->account_id, data->unique_id);
- investment_account = gnc_import_select_account(
- gnc_gen_trans_list_widget(info->gnc_ofx_importer_gui),
- acct_online_id, TRUE, inv_data.acct_text, inv_data.commodity,
- ACCT_TYPE_STOCK, info->last_investment_account, NULL);
+ investment_account =
+ gnc_import_select_account(GTK_WIDGET(info->parent), acct_online_id,
+ TRUE, inv_data.acct_text,
+ inv_data.commodity, ACCT_TYPE_STOCK,
+ info->last_investment_account, NULL);
if (investment_account)
info->last_investment_account = investment_account;
@@ -770,10 +771,11 @@ choose_income_account(Account* investment_account, Transaction *transaction,
income_account_text = g_strdup_printf(
_("Income account for security \"%s\""),
sanitize_string (data->security_data_ptr->secname));
- income_account = gnc_import_select_account(
- gnc_gen_trans_list_widget(info->gnc_ofx_importer_gui),
- NULL, TRUE, income_account_text, currency,
- ACCT_TYPE_INCOME, info->last_income_account, NULL);
+ income_account =
+ gnc_import_select_account(GTK_WIDGET(info->parent), NULL, TRUE,
+ income_account_text, currency,
+ ACCT_TYPE_INCOME,
+ info->last_income_account, NULL);
if (income_account != NULL)
{
@@ -934,7 +936,7 @@ int ofx_proc_transaction_cb(OfxTransactionData data, void *user_data)
Transaction *transaction;
ofx_info* info = (ofx_info*) user_data;
- g_assert(info->gnc_ofx_importer_gui);
+ g_assert(info->parent);
if (!data.amount_valid)
{
@@ -950,7 +952,7 @@ int ofx_proc_transaction_cb(OfxTransactionData data, void *user_data)
gnc_utf8_strip_invalid (data.account_id);
- import_account = gnc_import_select_account(gnc_gen_trans_list_widget(info->gnc_ofx_importer_gui),
+ import_account = gnc_import_select_account(GTK_WIDGET(info->parent),
data.account_id,
0, NULL, NULL, ACCT_TYPE_NONE,
info->last_import_account, NULL);
commit 862848380c788a23a26500562722a57a40c5a717
Author: John Ralls <jralls at ceridwen.us>
Date: Sat Dec 10 17:18:38 2022 -0800
Bug 798681 - Previously imported investment income transactions may not be filtered.
Resequence process_investment_transaction so that the first split is
the primary asset account split (cash unless it's a reinvest) followed
by the security asset account if it's not income and finally the income
split for income or reinvest transactions.
Note that there's also a sign change on the income splits for
income and reinvest transactions: testing showed the signs to be
backwards.
diff --git a/gnucash/import-export/ofx/gnc-ofx-import.c b/gnucash/import-export/ofx/gnc-ofx-import.c
index 688531266..c58463009 100644
--- a/gnucash/import-export/ofx/gnc-ofx-import.c
+++ b/gnucash/import-export/ofx/gnc-ofx-import.c
@@ -804,8 +804,9 @@ add_investment_split(Transaction* transaction, Account* account,
xaccTransAppendSplit(transaction, split);
xaccAccountInsertSplit(account, split);
- gnc_amount = gnc_ofx_numeric_from_double_txn(ofx_get_investment_amount(data),
- transaction);
+ gnc_amount =
+ gnc_ofx_numeric_from_double_txn(ofx_get_investment_amount(data),
+ transaction);
gnc_units = gnc_ofx_numeric_from_double (data->units, commodity);
xaccSplitSetAmount(split, gnc_units);
xaccSplitSetValue(split, gnc_amount);
@@ -854,77 +855,75 @@ add_currency_split(Transaction *transaction, Account* account,
gnc_import_set_split_online_id (split, sanitize_string (data->fi_id));
}
+/* ******** Process an investment transaction **********/
+/* Note that the ACCT_TYPE_STOCK account type
+ should be replaced with something derived from
+ data->invtranstype*/
+
static void
process_investment_transaction(Transaction *transaction, Account *import_account,
OfxTransactionData *data, ofx_info *info)
{
- Split *split;
- gnc_numeric gnc_amount, gnc_units;
- gnc_commodity *investment_commodity = NULL;
Account *investment_account = NULL;
Account *income_account = NULL;
- QofBook *book = qof_instance_get_book(QOF_INSTANCE(transaction));
+ gnc_commodity *investment_commodity;
+ double amount = data->amount;
+
+ g_return_if_fail(data->invtransactiontype_valid);
gnc_utf8_strip_invalid (data->unique_id);
- /********* Process an investment transaction **********/
- /* Note that the ACCT_TYPE_STOCK account type
- should be replaced with something derived from
- data->invtranstype*/
- // We have an investment transaction. First select the correct commodity.
+ // Set the cash split unless it's a reinvestment, which doesn't have one.
+ if (data->invtransactiontype != OFX_REINVEST)
+ {
+ DEBUG("Adding investment cash split.");
+ add_currency_split(transaction, import_account,
+ -ofx_get_investment_amount(data), data);
+ }
+
investment_commodity = gnc_import_select_commodity(data->unique_id,
- FALSE,
- NULL,
- NULL);
- if (investment_commodity != NULL)
- investment_account = choose_investment_account(data, info,
- investment_commodity);
- else
+ FALSE, NULL, NULL);
+ if (!investment_commodity)
{
PERR("Commodity not found for the investment transaction");
+ return;
}
+ investment_account = choose_investment_account(data, info,
+ investment_commodity);
- if (investment_account != NULL &&
- data->unitprice_valid &&
- data->units_valid &&
- ( data->invtransactiontype != OFX_INCOME ) )
- add_investment_split(transaction, investment_account, data);
- else
- PERR("The investment account, units or unitprice was not found for the investment transaction");
-
- if (data->invtransactiontype_valid && investment_account)
+ if (!investment_account)
{
- double amount = data->amount;
-#ifdef HAVE_LIBOFX_VERSION_0_10
- if (data->currency_ratio_valid && data->currency_ratio != 0)
- amount *= data->currency_ratio;
-#endif
- if (data->invtransactiontype == OFX_REINVEST
- || data->invtransactiontype == OFX_INCOME)
- income_account = choose_income_account(investment_account,
- transaction, data, info);
- if (income_account != NULL &&
- data->invtransactiontype == OFX_REINVEST)
- {
- DEBUG("Adding investment split; Money flows from the income account");
- add_currency_split(transaction, income_account, -amount, data);
- }
- if (income_account != NULL &&
- data->invtransactiontype == OFX_INCOME)
- {
- DEBUG("Adding investment split; Money flows to the income account");
- add_currency_split(transaction, income_account, amount, data);
- }
+ PERR("Failed to determine an investment asset account.");
+ return;
}
- if (data->invtransactiontype_valid
- && data->invtransactiontype != OFX_REINVEST)
+ if (data->invtransactiontype != OFX_INCOME)
{
- DEBUG("Adding investment split; Money flows from or to the cash account");
- add_currency_split(transaction, import_account,
- -ofx_get_investment_amount(data), data);
+ if (data->unitprice_valid && data->units_valid)
+ add_investment_split(transaction, investment_account, data);
+ else
+ PERR("Unable to add investment split, unit price or units were invalid.");
}
+
+ if (!(data->invtransactiontype == OFX_REINVEST
+ || data->invtransactiontype == OFX_INCOME))
+ //Done
+ return;
+
+#ifdef HAVE_LIBOFX_VERSION_0_10
+ if (data->currency_ratio_valid && data->currency_ratio != 0)
+ amount *= data->currency_ratio;
+#endif
+ income_account = choose_income_account(investment_account,
+ transaction, data, info);
+ g_return_if_fail(income_account);
+
+ DEBUG("Adding investment income split.");
+ if (data->invtransactiontype == OFX_REINVEST)
+ add_currency_split(transaction, income_account, amount, data);
+ else
+ add_currency_split(transaction, income_account, -amount, data);
}
int ofx_proc_transaction_cb(OfxTransactionData data, void *user_data)
commit 6a9e1cb24976c0a1a1b78f62adda2b3234d8915c
Author: John Ralls <jralls at ceridwen.us>
Date: Sat Dec 10 16:32:25 2022 -0800
[ofx import] Don't set online-id on income accounts.
diff --git a/gnucash/import-export/ofx/gnc-ofx-import.c b/gnucash/import-export/ofx/gnc-ofx-import.c
index 67537b0e1..688531266 100644
--- a/gnucash/import-export/ofx/gnc-ofx-import.c
+++ b/gnucash/import-export/ofx/gnc-ofx-import.c
@@ -825,7 +825,9 @@ add_investment_split(Transaction* transaction, Account* account,
xaccSplitSetMemo(split,
sanitize_string (data->security_data_ptr->memo));
}
- if (data->fi_id_valid)
+ if (data->fi_id_valid &&
+ xaccAccountTypesCompatible(xaccAccountGetType(account),
+ ACCT_TYPE_ASSET))
{
gnc_import_set_split_online_id(split,
sanitize_string (data->fi_id));
commit 17eb739da3eef1166e7c42adb8400d031db2baf5
Author: John Ralls <jralls at ceridwen.us>
Date: Thu Dec 8 13:38:16 2022 -0800
[ofx import] Extract functions from ofx_proc_transaction_cb.
set_transaction_dates, fill_transaction_description,
fill_transaction_notes, process_bank_transaction,
process_investment_transaction, choose_investment_account,
choose_income_account, add_investment_split, add_currency_split,
create_investment_subaccount, continue_account_selection,
choose_investment_account_helper, choose_investment_account
diff --git a/gnucash/import-export/ofx/gnc-ofx-import.c b/gnucash/import-export/ofx/gnc-ofx-import.c
index f7781c568..67537b0e1 100644
--- a/gnucash/import-export/ofx/gnc-ofx-import.c
+++ b/gnucash/import-export/ofx/gnc-ofx-import.c
@@ -415,76 +415,29 @@ fix_ofx_bug_39 (time64 t)
return t;
}
-int ofx_proc_transaction_cb(OfxTransactionData data, void *user_data)
+static void
+set_transaction_dates(Transaction *transaction, OfxTransactionData *data)
{
- char dest_string[255];
- time64 current_time = gnc_time (NULL);
- Account *account;
- Account *investment_account = NULL;
- Account *income_account = NULL;
- gchar *investment_account_text, *investment_account_onlineid;
- gnc_commodity *currency = NULL;
- gnc_commodity *investment_commodity = NULL;
- gnc_numeric gnc_amount, gnc_units;
- QofBook *book;
- Transaction *transaction;
- Split *split;
- gchar *notes, *tmp;
- ofx_info* info = (ofx_info*) user_data;
- GtkWindow *parent = GTK_WINDOW (info->parent);
-
- g_assert(info->gnc_ofx_importer_gui);
-
- if (!data.account_id_valid)
- {
- PERR("account ID for this transaction is unavailable!");
- return 0;
- }
- else
- gnc_utf8_strip_invalid (data.account_id);
-
- account = gnc_import_select_account(gnc_gen_trans_list_widget(info->gnc_ofx_importer_gui),
- data.account_id,
- 0, NULL, NULL, ACCT_TYPE_NONE,
- info->last_import_account, NULL);
- if (account == NULL)
- {
- PERR("Unable to find account for id %s", data.account_id);
- return 0;
- }
- info->last_import_account = account;
- /***** Validate the input strings to ensure utf8 *****/
- if (data.name_valid)
- gnc_utf8_strip_invalid(data.name);
- if (data.memo_valid)
- gnc_utf8_strip_invalid(data.memo);
- if (data.check_number_valid)
- gnc_utf8_strip_invalid(data.check_number);
- if (data.reference_number_valid)
- gnc_utf8_strip_invalid(data.reference_number);
-
- /***** Create the transaction and setup transaction data *******/
- book = gnc_account_get_book(account);
- transaction = xaccMallocTransaction(book);
- xaccTransBeginEdit(transaction);
-
- /* Note: Unfortunately libofx <= 0.9.5 will not report a missing
+ /* Note: Unfortunately libofx <= 0.9.5 will not report a missing
* date field as an invalid one. Instead, it will report it as
* valid and return a completely bogus date. Starting with
* libofx-0.9.6 (not yet released as of 2012-09-09), it will still
* be reported as valid but at least the date integer itself is
* just plain zero. */
- if (data.date_posted_valid && (data.date_posted != 0))
+
+ time64 current_time = gnc_time (NULL);
+
+ if (data->date_posted_valid && (data->date_posted != 0))
{
/* The hopeful case: We have a posted_date */
- data.date_posted = fix_ofx_bug_39 (data.date_posted);
- xaccTransSetDatePostedSecsNormalized(transaction, data.date_posted);
+ data->date_posted = fix_ofx_bug_39 (data->date_posted);
+ xaccTransSetDatePostedSecsNormalized(transaction, data->date_posted);
}
- else if (data.date_initiated_valid && (data.date_initiated != 0))
+ else if (data->date_initiated_valid && (data->date_initiated != 0))
{
/* No posted date? Maybe we have an initiated_date */
- data.date_initiated = fix_ofx_bug_39 (data.date_initiated);
- xaccTransSetDatePostedSecsNormalized(transaction, data.date_initiated);
+ data->date_initiated = fix_ofx_bug_39 (data->date_initiated);
+ xaccTransSetDatePostedSecsNormalized(transaction, data->date_initiated);
}
else
{
@@ -493,462 +446,587 @@ int ofx_proc_transaction_cb(OfxTransactionData data, void *user_data)
}
xaccTransSetDateEnteredSecs(transaction, current_time);
+}
+static void
+fill_transaction_description(Transaction *transaction, OfxTransactionData *data)
+{
/* Put transaction name in Description, or memo if name unavailable */
- if (data.name_valid)
+ if (data->name_valid)
{
- xaccTransSetDescription(transaction, data.name);
+ xaccTransSetDescription(transaction, data->name);
}
- else if (data.memo_valid)
+ else if (data->memo_valid)
{
- xaccTransSetDescription(transaction, data.memo);
+ xaccTransSetDescription(transaction, data->memo);
}
+}
+static void
+fill_transaction_notes(Transaction *transaction, OfxTransactionData *data)
+{
/* Put everything else in the Notes field */
- notes = g_strdup_printf("OFX ext. info: ");
+ char *notes = g_strdup_printf("OFX ext. info: ");
- if (data.transactiontype_valid)
+ if (data->transactiontype_valid)
{
- tmp = notes;
+ char *tmp = notes;
notes = g_strdup_printf("%s%s%s", tmp, "|Trans type:",
- gnc_ofx_ttype_to_string(data.transactiontype));
+ gnc_ofx_ttype_to_string(data->transactiontype));
g_free(tmp);
}
- if (data.invtransactiontype_valid)
+ if (data->invtransactiontype_valid)
{
- tmp = notes;
+ char *tmp = notes;
notes = g_strdup_printf("%s%s%s", tmp, "|Investment Trans type:",
- gnc_ofx_invttype_to_str(data.invtransactiontype));
+ gnc_ofx_invttype_to_str(data->invtransactiontype));
g_free(tmp);
}
- if (data.memo_valid && data.name_valid) /* Copy only if memo wasn't put in Description */
+ if (data->memo_valid && data->name_valid) /* Copy only if memo wasn't put in Description */
{
- tmp = notes;
- notes = g_strdup_printf("%s%s%s", tmp, "|Memo:", data.memo);
+ char *tmp = notes;
+ notes = g_strdup_printf("%s%s%s", tmp, "|Memo:", data->memo);
g_free(tmp);
}
- if (data.date_funds_available_valid)
+ if (data->date_funds_available_valid)
{
- time64 time = data.date_funds_available;
+ char dest_string[MAX_DATE_LENGTH];
+ time64 time = data->date_funds_available;
+ char *tmp = notes;
+
gnc_time64_to_iso8601_buff (time, dest_string);
- tmp = notes;
notes = g_strdup_printf("%s%s%s", tmp,
"|Date funds available:", dest_string);
g_free(tmp);
}
- if (data.server_transaction_id_valid)
+ if (data->server_transaction_id_valid)
{
- tmp = notes;
+ char *tmp = notes;
notes = g_strdup_printf("%s%s%s", tmp,
"|Server trans ID (conf. number):",
- sanitize_string (data.server_transaction_id));
+ sanitize_string (data->server_transaction_id));
g_free(tmp);
}
- if (data.standard_industrial_code_valid)
+ if (data->standard_industrial_code_valid)
{
- tmp = notes;
+ char *tmp = notes;
notes = g_strdup_printf("%s%s%ld", tmp,
"|Standard Industrial Code:",
- data.standard_industrial_code);
+ data->standard_industrial_code);
g_free(tmp);
}
- if (data.payee_id_valid)
+ if (data->payee_id_valid)
{
- tmp = notes;
+ char *tmp = notes;
notes = g_strdup_printf("%s%s%s", tmp, "|Payee ID:",
- sanitize_string (data.payee_id));
+ sanitize_string (data->payee_id));
g_free(tmp);
}
-
//PERR("WRITEME: GnuCash ofx_proc_transaction():Add PAYEE and ADDRESS here once supported by libofx! Notes=%s\n", notes);
/* Ideally, gnucash should process the corrected transactions */
- if (data.fi_id_corrected_valid)
+ if (data->fi_id_corrected_valid)
{
+ char *tmp = notes;
PERR("WRITEME: GnuCash ofx_proc_transaction(): WARNING: This transaction corrected a previous transaction, but we created a new one instead!\n");
- tmp = notes;
notes = g_strdup_printf("%s%s%s%s", tmp,
"|This corrects transaction #",
- sanitize_string (data.fi_id_corrected),
+ sanitize_string (data->fi_id_corrected),
"but GnuCash didn't process the correction!");
g_free(tmp);
}
xaccTransSetNotes(transaction, notes);
g_free(notes);
- if (data.account_ptr && data.account_ptr->currency_valid)
+}
+
+static void
+process_bank_transaction(Transaction *transaction, Account *import_account,
+ OfxTransactionData *data, ofx_info *info)
+{
+ Split *split;
+ gnc_numeric gnc_amount;
+ QofBook *book = qof_instance_get_book(QOF_INSTANCE(transaction));
+ double amount = data->amount;
+#ifdef HAVE_LIBOFX_VERSION_0_10
+ if (data->currency_ratio_valid && data->currency_ratio != 0)
+ amount *= data->currency_ratio;
+#endif
+ /***** Process a normal transaction ******/
+ DEBUG("Adding split; Ordinary banking transaction, money flows from or into the source account");
+ split = xaccMallocSplit(book);
+ xaccTransAppendSplit(transaction, split);
+ xaccAccountInsertSplit(import_account, split);
+ gnc_amount = gnc_ofx_numeric_from_double_txn(amount, transaction);
+ xaccSplitSetBaseValue(split, gnc_amount, xaccTransGetCurrency(transaction));
+
+ /* set tran-num and/or split-action per book option */
+ if (data->check_number_valid)
{
- DEBUG("Currency from libofx: %s", data.account_ptr->currency);
- currency = gnc_commodity_table_lookup( gnc_get_current_commodities (),
- GNC_COMMODITY_NS_CURRENCY,
- data.account_ptr->currency);
+ /* SQL will correctly interpret the string "null", but
+ * the transaction num field is declared to be
+ * non-null so substitute the empty string.
+ */
+ const char *num_value =
+ strcasecmp (data->check_number, "null") == 0 ? "" :
+ data->check_number;
+ gnc_set_num_action(transaction, split, num_value, NULL);
+ }
+ else if (data->reference_number_valid)
+ {
+ const char *num_value =
+ strcasecmp (data->reference_number, "null") == 0 ? "" :
+ data->check_number;
+ gnc_set_num_action(transaction, split, num_value, NULL);
+ }
+ /* Also put the ofx transaction's memo in the
+ * split's memo field */
+ if (data->memo_valid)
+ {
+ xaccSplitSetMemo(split, data->memo);
+ }
+ if (data->fi_id_valid)
+ {
+ gnc_import_set_split_online_id(split,
+ sanitize_string (data->fi_id));
+ }
+}
+
+typedef struct
+{
+ gnc_commodity *commodity;
+ char *online_id;
+ char *acct_text;
+ gboolean choosing;
+} InvestmentAcctData;
+
+static Account*
+create_investment_subaccount(GtkWindow *parent, Account* parent_acct,
+ InvestmentAcctData *inv_data)
+{
+
+ Account *investment_account =
+ gnc_ofx_new_account(parent,
+ inv_data->acct_text,
+ inv_data->commodity,
+ parent_acct,
+ ACCT_TYPE_STOCK);
+ if (investment_account)
+ {
+ gnc_import_set_acc_online_id(investment_account, inv_data->online_id);
+ inv_data->choosing = FALSE;
+ ofx_parent_account = parent_acct;
}
else
{
- DEBUG("Currency from libofx unavailable, defaulting to account's default");
- currency = xaccAccountGetCommodity(account);
+ ofx_parent_account = NULL;
}
+ return investment_account;
+}
- xaccTransSetCurrency(transaction, currency);
- if (data.amount_valid)
+static gboolean
+continue_account_selection(GNCImportMainMatcher* matcher, Account* account,
+ gnc_commodity* commodity)
+{
+ gboolean keep_going =
+ gnc_verify_dialog(
+ GTK_WINDOW (gnc_gen_trans_list_widget(matcher)), 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(account),
+ gnc_commodity_get_fullname(commodity),
+ gnc_commodity_get_fullname(xaccAccountGetCommodity(account)));
+ // We must also delete the online_id that was set in gnc_import_select_account()
+ gnc_import_set_acc_online_id(account, "");
+ return keep_going;
+}
+
+static Account*
+choose_investment_account_helper(OfxTransactionData *data, ofx_info *info,
+ InvestmentAcctData *inv_data)
+{
+ Account *investment_account = NULL;
+ // 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)
{
- if (!data.invtransactiontype_valid
-#ifdef HAVE_LIBOFX_VERSION_0_10
- || data.invtransactiontype == OFX_INVBANKTRAN)
-#else
- )
-#endif
+ // 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(info->gnc_ofx_importer_gui),
+ data->unique_id, TRUE, inv_data->acct_text,
+ inv_data->commodity, ACCT_TYPE_STOCK,
+ info->last_investment_account, &inv_data->choosing);
+ if (investment_account)
+ info->last_investment_account = investment_account;
+ }
+ // Does the chosen account have the right commodity?
+ if (investment_account &&
+ xaccAccountGetCommodity(investment_account) == inv_data->commodity)
+ return investment_account;
+
+ if (!investment_account) // Nothing more to try
+ return NULL;
+
+ if (auto_create_commodity &&
+ xaccAccountTypesCompatible(xaccAccountGetType(investment_account),
+ ACCT_TYPE_STOCK))
+ {
+ /* We have an account but the commodity doesn't match. Since
+ * auto_create_commodity is on and the account has a
+ * compatible type we can just create a sub-account with the
+ * right commodity.
+ */
+ investment_account =
+ create_investment_subaccount(GTK_WINDOW(info->parent),
+ investment_account,
+ inv_data);
+ }
+ else
+ {
+ // No account with matching commodity. Ask the user
+ // whether to continue or abort.
+ inv_data->choosing =
+ continue_account_selection(info->gnc_ofx_importer_gui,
+ investment_account,
+ inv_data->commodity);
+ investment_account = NULL;
+ }
+
+ return investment_account;
+}
+
+static Account*
+choose_investment_account(OfxTransactionData *data, ofx_info *info,
+ gnc_commodity *commodity)
+{
+ Account* investment_account = NULL;
+ gnc_numeric gnc_amount, gnc_units;
+ InvestmentAcctData inv_data = {commodity, NULL, NULL, FALSE};
+ char *acct_online_id;
+ // As we now have the commodity, select the account with that commodity.
+
+ /* Translators: This string is a default account name. It MUST
+ * NOT contain the character ':' anywhere in it or in any
+ * translations. */
+ inv_data.acct_text = g_strdup_printf(
+ _("Stock account for security \"%s\""),
+ sanitize_string (data->security_data_ptr->secname));
+ inv_data.online_id = data->unique_id;
+
+ acct_online_id = g_strdup_printf("%s%s", data->account_id, data->unique_id);
+ investment_account = gnc_import_select_account(
+ gnc_gen_trans_list_widget(info->gnc_ofx_importer_gui),
+ acct_online_id, TRUE, inv_data.acct_text, inv_data.commodity,
+ ACCT_TYPE_STOCK, info->last_investment_account, NULL);
+ if (investment_account)
+ info->last_investment_account = investment_account;
+
+ // but use it only if that's really the right commodity
+ if (investment_account
+ && xaccAccountGetCommodity(investment_account) != inv_data.commodity)
+ investment_account = NULL;
+
+ // Loop until we either have an account, or the user pressed Cancel
+ while (!investment_account && inv_data.choosing)
+ investment_account = choose_investment_account_helper(data, info,
+ &inv_data);
+ if (!investment_account)
+ {
+ PERR("No investment account found for text: %s\n", inv_data.acct_text);
+ }
+ g_free (inv_data.acct_text);
+ g_free (acct_online_id);
+
+ return investment_account;
+}
+
+static Account*
+choose_income_account(Account* investment_account, Transaction *transaction,
+ OfxTransactionData *data, ofx_info *info)
+{
+ Account *income_account = NULL;
+ DEBUG("Now let's find an account for the destination split");
+ income_account =
+ get_associated_income_account(investment_account);
+
+ if (income_account == NULL)
+ {
+ char *income_account_text;
+ gnc_commodity *currency = xaccTransGetCurrency(transaction);
+ DEBUG("Couldn't find an associated income account");
+ /* Translators: This string is a default account
+ * name. It MUST NOT contain the character ':' anywhere
+ * in it or in any translations. */
+ income_account_text = g_strdup_printf(
+ _("Income account for security \"%s\""),
+ sanitize_string (data->security_data_ptr->secname));
+ income_account = gnc_import_select_account(
+ gnc_gen_trans_list_widget(info->gnc_ofx_importer_gui),
+ NULL, TRUE, income_account_text, currency,
+ ACCT_TYPE_INCOME, info->last_income_account, NULL);
+
+ if (income_account != NULL)
{
- double amount = data.amount;
-#ifdef HAVE_LIBOFX_VERSION_0_10
- if (data.currency_ratio_valid && data.currency_ratio != 0)
- amount *= data.currency_ratio;
-#endif
- /***** Process a normal transaction ******/
- DEBUG("Adding split; Ordinary banking transaction, money flows from or into the source account");
- split = xaccMallocSplit(book);
- xaccTransAppendSplit(transaction, split);
- xaccAccountInsertSplit(account, split);
- gnc_amount = gnc_ofx_numeric_from_double_txn(amount, transaction);
- xaccSplitSetBaseValue(split, gnc_amount, xaccTransGetCurrency(transaction));
-
- /* set tran-num and/or split-action per book option */
- if (data.check_number_valid)
- {
- /* SQL will correctly interpret the string "null", but
- * the transaction num field is declared to be
- * non-null so substitute the empty string.
- */
- const char *num_value =
- strcasecmp (data.check_number, "null") == 0 ? "" :
- data.check_number;
- gnc_set_num_action(transaction, split, num_value, NULL);
- }
- else if (data.reference_number_valid)
- {
- const char *num_value =
- strcasecmp (data.reference_number, "null") == 0 ? "" :
- data.check_number;
- gnc_set_num_action(transaction, split, num_value, NULL);
- }
- /* Also put the ofx transaction's memo in the
- * split's memo field */
- if (data.memo_valid)
- {
- xaccSplitSetMemo(split, data.memo);
- }
- if (data.fi_id_valid)
- {
- gnc_import_set_split_online_id(split,
- sanitize_string (data.fi_id));
- }
+ info->last_income_account = income_account;
+ set_associated_income_account(investment_account,
+ income_account);
+ DEBUG("KVP written");
}
+ }
+ else
+ {
+ DEBUG("Found at least one associated income account");
+ }
- 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;
- gnc_utf8_strip_invalid (data.unique_id);
- /********* Process an investment transaction **********/
- /* Note that the ACCT_TYPE_STOCK account type
- should be replaced with something derived from
- data.invtranstype*/
-
- // We have an investment transaction. First select the correct commodity.
- investment_commodity = gnc_import_select_commodity(data.unique_id,
- FALSE,
- NULL,
- NULL);
- if (investment_commodity != NULL)
- {
- // As we now have the commodity, select the account with that commodity.
-
- /* Translators: This string is a default account name. It MUST
- * NOT contain the character ':' anywhere in it or in any
- * translations. */
- investment_account_text = g_strdup_printf(
- _("Stock account for security \"%s\""),
- sanitize_string (data.security_data_ptr->secname));
-
- investment_account_onlineid = g_strdup_printf( "%s%s",
- data.account_id,
- data.unique_id);
- investment_account = gnc_import_select_account(
- gnc_gen_trans_list_widget(info->gnc_ofx_importer_gui),
- investment_account_onlineid,
- 1,
- investment_account_text,
- investment_commodity,
- ACCT_TYPE_STOCK,
- info->last_investment_account,
- NULL);
- if (investment_account)
- info->last_investment_account = investment_account;
-
- // 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(info->gnc_ofx_importer_gui),
- data.unique_id,
- TRUE,
- investment_account_text,
- investment_commodity,
- ACCT_TYPE_STOCK,
- info->last_investment_account,
- &choosing_account);
- if (investment_account)
- info->last_investment_account = investment_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(parent,
- 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(
- GTK_WINDOW (gnc_gen_trans_list_widget(info->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);
- g_free (investment_account_onlineid);
- investment_account_text = NULL;
-
- if (investment_account != NULL &&
- data.unitprice_valid &&
- data.units_valid &&
- ( data.invtransactiontype != OFX_INCOME ) )
- {
- DEBUG("Adding investment split; Money flows from or into the stock account");
- split = xaccMallocSplit(book);
- xaccTransAppendSplit(transaction, split);
- xaccAccountInsertSplit(investment_account, split);
-
- gnc_amount = gnc_ofx_numeric_from_double_txn (ofx_get_investment_amount(&data),
- transaction);
- gnc_units = gnc_ofx_numeric_from_double (data.units, investment_commodity);
- xaccSplitSetAmount(split, gnc_units);
- xaccSplitSetValue(split, gnc_amount);
-
- /* set tran-num and/or split-action per book option */
- if (data.check_number_valid)
- {
- gnc_set_num_action(transaction, split, data.check_number, NULL);
- }
- else if (data.reference_number_valid)
- {
- gnc_set_num_action(transaction, split,
- data.reference_number, NULL);
- }
- if (data.security_data_ptr->memo_valid)
- {
- xaccSplitSetMemo(split,
- sanitize_string (data.security_data_ptr->memo));
- }
- if (data.fi_id_valid)
- {
- gnc_import_set_split_online_id(split,
- sanitize_string (data.fi_id));
- }
- }
- else
- {
- if (investment_account)
- PERR("The investment account, units or unitprice was not found for the investment transaction");
- }
- }
- else
- {
- PERR("Commodity not found for the investment transaction");
- }
+ return income_account;
+}
- if (data.invtransactiontype_valid && investment_account)
- {
- double amount = data.amount;
-#ifdef HAVE_LIBOFX_VERSION_0_10
- if (data.currency_ratio_valid && data.currency_ratio != 0)
- amount *= data.currency_ratio;
-#endif
- if (data.invtransactiontype == OFX_REINVEST
- || data.invtransactiontype == OFX_INCOME)
- {
- DEBUG("Now let's find an account for the destination split");
- income_account =
- get_associated_income_account(investment_account);
-
- if (income_account == NULL)
- {
- DEBUG("Couldn't find an associated income account");
- /* Translators: This string is a default account
- * name. It MUST NOT contain the character ':' anywhere
- * in it or in any translations. */
- investment_account_text = g_strdup_printf(
- _("Income account for security \"%s\""),
- sanitize_string (data.security_data_ptr->secname));
- income_account = gnc_import_select_account(
- gnc_gen_trans_list_widget(info->gnc_ofx_importer_gui),
- NULL,
- 1,
- investment_account_text,
- currency,
- ACCT_TYPE_INCOME,
- info->last_income_account,
- NULL);
- if (income_account != NULL)
- {
- info->last_income_account = income_account;
- set_associated_income_account(investment_account,
- income_account);
- DEBUG("KVP written");
- }
-
- }
- else
- {
- DEBUG("Found at least one associated income account");
- }
- }
- if (income_account != NULL &&
- data.invtransactiontype == OFX_REINVEST)
- {
- DEBUG("Adding investment split; Money flows from the income account");
- split = xaccMallocSplit(book);
- xaccTransAppendSplit(transaction, split);
- xaccAccountInsertSplit(income_account, split);
- gnc_amount = gnc_ofx_numeric_from_double_txn(amount,
- transaction);
- xaccSplitSetBaseValue(split, gnc_amount, xaccTransGetCurrency(transaction));
-
- // Set split memo from ofx transaction name or memo
- gnc_ofx_set_split_memo(&data, split);
- if (data.fi_id_valid)
- gnc_import_set_split_online_id (split, sanitize_string (data.fi_id));
- }
- if (income_account != NULL &&
- data.invtransactiontype == OFX_INCOME)
- {
- DEBUG("Adding investment split; Money flows from the income account");
- split = xaccMallocSplit(book);
- xaccTransAppendSplit(transaction, split);
- xaccAccountInsertSplit(income_account, split);
- /*OFX_INCOME amounts come in as positive numbers*/
- gnc_amount = gnc_ofx_numeric_from_double_txn (-amount,
- transaction);
-
- xaccSplitSetBaseValue(split, gnc_amount, xaccTransGetCurrency(transaction));
-
- // Set split memo from ofx transaction name or memo
- gnc_ofx_set_split_memo(&data, split);
- if (data.fi_id_valid)
- gnc_import_set_split_online_id (split, sanitize_string (data.fi_id));
- }
- }
+static void
+add_investment_split(Transaction* transaction, Account* account,
+ OfxTransactionData *data)
+{
+ Split *split;
+ QofBook *book = gnc_account_get_book(account);
+ gnc_numeric gnc_amount, gnc_units;
+ gnc_commodity *commodity = xaccAccountGetCommodity(account);
+ DEBUG("Adding investment split; Money flows from or into the stock account");
+ split = xaccMallocSplit(book);
+ xaccTransAppendSplit(transaction, split);
+ xaccAccountInsertSplit(account, split);
+
+ gnc_amount = gnc_ofx_numeric_from_double_txn(ofx_get_investment_amount(data),
+ transaction);
+ gnc_units = gnc_ofx_numeric_from_double (data->units, commodity);
+ xaccSplitSetAmount(split, gnc_units);
+ xaccSplitSetValue(split, gnc_amount);
+
+ /* set tran-num and/or split-action per book option */
+ if (data->check_number_valid)
+ {
+ gnc_set_num_action(transaction, split, data->check_number, NULL);
+ }
+ else if (data->reference_number_valid)
+ {
+ gnc_set_num_action(transaction, split,
+ data->reference_number, NULL);
+ }
+ if (data->security_data_ptr->memo_valid)
+ {
+ xaccSplitSetMemo(split,
+ sanitize_string (data->security_data_ptr->memo));
+ }
+ if (data->fi_id_valid)
+ {
+ gnc_import_set_split_online_id(split,
+ sanitize_string (data->fi_id));
+ }
+}
- 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);
-
- 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);
- if (data.fi_id_valid)
- gnc_import_set_split_online_id (split, sanitize_string (data.fi_id));
- }
- }
+static void
+add_currency_split(Transaction *transaction, Account* account,
+ double amount, OfxTransactionData *data)
+{
+ Split *split;
+ QofBook *book = gnc_account_get_book(account);
+ gnc_numeric gnc_amount;
+
+ split = xaccMallocSplit(book);
+ xaccTransAppendSplit(transaction, split);
+ xaccAccountInsertSplit(account, split);
+ gnc_amount = gnc_ofx_numeric_from_double_txn(amount, transaction);
+ xaccSplitSetBaseValue(split, gnc_amount, xaccTransGetCurrency(transaction));
+
+ // Set split memo from ofx transaction name or memo
+ gnc_ofx_set_split_memo(data, split);
+ if (data->fi_id_valid)
+ gnc_import_set_split_online_id (split, sanitize_string (data->fi_id));
+}
+
+static void
+process_investment_transaction(Transaction *transaction, Account *import_account,
+ OfxTransactionData *data, ofx_info *info)
+{
+ Split *split;
+ gnc_numeric gnc_amount, gnc_units;
+ gnc_commodity *investment_commodity = NULL;
+ Account *investment_account = NULL;
+ Account *income_account = NULL;
+ QofBook *book = qof_instance_get_book(QOF_INSTANCE(transaction));
+
+ gnc_utf8_strip_invalid (data->unique_id);
+
+ /********* Process an investment transaction **********/
+ /* Note that the ACCT_TYPE_STOCK account type
+ should be replaced with something derived from
+ data->invtranstype*/
+
+ // We have an investment transaction. First select the correct commodity.
+ investment_commodity = gnc_import_select_commodity(data->unique_id,
+ FALSE,
+ NULL,
+ NULL);
+ if (investment_commodity != NULL)
+ investment_account = choose_investment_account(data, info,
+ investment_commodity);
+ else
+ {
+ PERR("Commodity not found for the investment transaction");
+ }
+
+ if (investment_account != NULL &&
+ data->unitprice_valid &&
+ data->units_valid &&
+ ( data->invtransactiontype != OFX_INCOME ) )
+ add_investment_split(transaction, investment_account, data);
+ else
+ PERR("The investment account, units or unitprice was not found for the investment transaction");
- /* Send transaction to importer GUI. */
- if (xaccTransCountSplits(transaction) > 0)
+ if (data->invtransactiontype_valid && investment_account)
+ {
+ double amount = data->amount;
+#ifdef HAVE_LIBOFX_VERSION_0_10
+ if (data->currency_ratio_valid && data->currency_ratio != 0)
+ amount *= data->currency_ratio;
+#endif
+ if (data->invtransactiontype == OFX_REINVEST
+ || data->invtransactiontype == OFX_INCOME)
+ income_account = choose_income_account(investment_account,
+ transaction, data, info);
+ if (income_account != NULL &&
+ data->invtransactiontype == OFX_REINVEST)
{
- DEBUG("%d splits sent to the importer gui",
- xaccTransCountSplits(transaction));
- info->trans_list = g_list_prepend (info->trans_list, transaction);
+ DEBUG("Adding investment split; Money flows from the income account");
+ add_currency_split(transaction, income_account, -amount, data);
}
- else
+ if (income_account != NULL &&
+ data->invtransactiontype == OFX_INCOME)
{
- PERR("No splits in transaction (missing account?), ignoring.");
- xaccTransDestroy(transaction);
- xaccTransCommitEdit(transaction);
+ DEBUG("Adding investment split; Money flows to the income account");
+ add_currency_split(transaction, income_account, amount, data);
}
}
- else
+
+ if (data->invtransactiontype_valid
+ && data->invtransactiontype != OFX_REINVEST)
+ {
+ DEBUG("Adding investment split; Money flows from or to the cash account");
+ add_currency_split(transaction, import_account,
+ -ofx_get_investment_amount(data), data);
+ }
+}
+
+int ofx_proc_transaction_cb(OfxTransactionData data, void *user_data)
+{
+ Account *import_account;
+ gnc_commodity *currency = NULL;
+ QofBook *book;
+ Transaction *transaction;
+ ofx_info* info = (ofx_info*) user_data;
+
+ g_assert(info->gnc_ofx_importer_gui);
+
+ if (!data.amount_valid)
{
PERR("The transaction doesn't have a valid amount");
+ return 0;
+ }
+
+ if (!data.account_id_valid)
+ {
+ PERR("account ID for this transaction is unavailable!");
+ return 0;
+ }
+
+ gnc_utf8_strip_invalid (data.account_id);
+
+ import_account = gnc_import_select_account(gnc_gen_trans_list_widget(info->gnc_ofx_importer_gui),
+ data.account_id,
+ 0, NULL, NULL, ACCT_TYPE_NONE,
+ info->last_import_account, NULL);
+ if (import_account == NULL)
+ {
+ PERR("Unable to find account for id %s", data.account_id);
+ return 0;
+ }
+ info->last_import_account = import_account;
+ /***** Validate the input strings to ensure utf8 *****/
+ if (data.name_valid)
+ gnc_utf8_strip_invalid(data.name);
+ if (data.memo_valid)
+ gnc_utf8_strip_invalid(data.memo);
+ if (data.check_number_valid)
+ gnc_utf8_strip_invalid(data.check_number);
+ if (data.reference_number_valid)
+ gnc_utf8_strip_invalid(data.reference_number);
+
+ /***** Create the transaction and setup transaction data *******/
+ book = gnc_account_get_book(import_account);
+ transaction = xaccMallocTransaction(book);
+ xaccTransBeginEdit(transaction);
+
+ set_transaction_dates(transaction, &data);
+ fill_transaction_description(transaction, &data);
+ fill_transaction_notes(transaction, &data);
+
+ if (data.account_ptr && data.account_ptr->currency_valid)
+ {
+ DEBUG("Currency from libofx: %s", data.account_ptr->currency);
+ currency = gnc_commodity_table_lookup( gnc_get_current_commodities (),
+ GNC_COMMODITY_NS_CURRENCY,
+ data.account_ptr->currency);
+ }
+ else
+ {
+ DEBUG("Currency from libofx unavailable, defaulting to account's default");
+ currency = xaccAccountGetCommodity(import_account);
+ }
+
+ xaccTransSetCurrency(transaction, currency);
+
+ if (!data.invtransactiontype_valid
+#ifdef HAVE_LIBOFX_VERSION_0_10
+ || data.invtransactiontype == OFX_INVBANKTRAN
+#endif
+ )
+ process_bank_transaction(transaction, import_account, &data, info);
+ else if (data.unique_id_valid
+ && data.security_data_valid
+ && data.security_data_ptr != NULL
+ && data.security_data_ptr->secname_valid)
+ process_investment_transaction(transaction, import_account,
+ &data, info);
+ else
+ {
+ PERR("Unsupported OFX transaction type.");
+ xaccTransDestroy(transaction);
+ xaccTransCommitEdit(transaction);
+ return 0;
+ }
+
+ /* Send transaction to importer GUI. */
+ if (xaccTransCountSplits(transaction) > 0)
+ {
+ DEBUG("%d splits sent to the importer gui",
+ xaccTransCountSplits(transaction));
+ info->trans_list = g_list_prepend (info->trans_list, transaction);
+ }
+ else
+ {
+ PERR("No splits in transaction (missing account?), ignoring.");
xaccTransDestroy(transaction);
xaccTransCommitEdit(transaction);
}
+
info->num_trans_processed += 1;
return 0;
}//end ofx_proc_transaction()
commit 0bc2d692c732048f6950c514ce8d126c82810dfa
Author: John Ralls <jralls at ceridwen.us>
Date: Thu Dec 8 13:09:10 2022 -0800
[ofx import] Typedef OfxTransactionData.
So we don't have to say struct on every use.
diff --git a/gnucash/import-export/ofx/gnc-ofx-import.c b/gnucash/import-export/ofx/gnc-ofx-import.c
index c1f0bd205..f7781c568 100644
--- a/gnucash/import-export/ofx/gnc-ofx-import.c
+++ b/gnucash/import-export/ofx/gnc-ofx-import.c
@@ -65,6 +65,9 @@ static QofLogModule log_module = GNC_MOD_IMPORT;
static gboolean auto_create_commodity = FALSE;
static Account *ofx_parent_account = NULL;
+
+typedef struct OfxTransactionData OfxTransactionData;
+
// Structure we use to gather information about statement balance/account etc.
typedef struct _ofx_info
{
@@ -126,9 +129,9 @@ set_associated_income_account(Account* investment_account,
int ofx_proc_statement_cb (struct OfxStatementData data, void * statement_user_data);
int ofx_proc_security_cb (const struct OfxSecurityData data, void * security_user_data);
-int ofx_proc_transaction_cb (struct OfxTransactionData data, void *user_data);
+int ofx_proc_transaction_cb (OfxTransactionData data, void *user_data);
int ofx_proc_account_cb (struct OfxAccountData data, void * account_user_data);
-static double ofx_get_investment_amount (const struct OfxTransactionData* data);
+static double ofx_get_investment_amount (const OfxTransactionData* data);
static const gchar *gnc_ofx_ttype_to_string(TransactionType t)
{
@@ -314,7 +317,7 @@ int ofx_proc_security_cb(const struct OfxSecurityData data, void * security_user
return 0;
}
-static void gnc_ofx_set_split_memo(const struct OfxTransactionData* data, Split *split)
+static void gnc_ofx_set_split_memo(const OfxTransactionData* data, Split *split)
{
g_assert(data);
g_assert(split);
@@ -412,7 +415,7 @@ fix_ofx_bug_39 (time64 t)
return t;
}
-int ofx_proc_transaction_cb(struct OfxTransactionData data, void *user_data)
+int ofx_proc_transaction_cb(OfxTransactionData data, void *user_data)
{
char dest_string[255];
time64 current_time = gnc_time (NULL);
@@ -1079,7 +1082,7 @@ int ofx_proc_account_cb(struct OfxAccountData data, void * account_user_data)
return 0;
}
-double ofx_get_investment_amount(const struct OfxTransactionData* data)
+double ofx_get_investment_amount(const OfxTransactionData* data)
{
double amount = data->amount;
#ifdef HAVE_LIBOFX_VERSION_0_10
commit 5519a9d788c1ec6439252daf956d16c299dcd651
Author: John Ralls <jralls at ceridwen.us>
Date: Sat Dec 10 16:26:05 2022 -0800
[import matcher] Make related functions adjacent
gnc_gen_trans_list_add_trans just wraps
gnc_gen_trans_list_add_trans_with_ref_id.
diff --git a/gnucash/import-export/import-main-matcher.c b/gnucash/import-export/import-main-matcher.c
index bb4dfbe0d..ababddff6 100644
--- a/gnucash/import-export/import-main-matcher.c
+++ b/gnucash/import-export/import-main-matcher.c
@@ -2189,21 +2189,6 @@ refresh_model_row (GNCImportMainMatcher *gui,
gtk_tree_selection_unselect_all (selection);
}
-void
-gnc_gen_trans_list_add_trans (GNCImportMainMatcher *gui, Transaction *trans)
-{
- Account* acc = NULL;
- Split* split = NULL;
- int i=0;
-
- split = xaccTransGetSplit (trans, 0);
- acc = xaccSplitGetAccount (split);
- defer_bal_computation (gui, acc);
-
- gnc_gen_trans_list_add_trans_with_ref_id (gui, trans, 0);
- return;
-}/* end gnc_import_add_trans() */
-
void
gnc_gen_trans_list_show_reconcile_after_close_button (GNCImportMainMatcher *info,
gboolean reconcile_after_close,
@@ -2219,6 +2204,22 @@ gnc_gen_trans_list_get_reconcile_after_close_button (GNCImportMainMatcher *info)
return info->reconcile_after_close;
}
+
+void
+gnc_gen_trans_list_add_trans (GNCImportMainMatcher *gui, Transaction *trans)
+{
+ Account* acc = NULL;
+ Split* split = NULL;
+ int i=0;
+
+ split = xaccTransGetSplit (trans, 0);
+ acc = xaccSplitGetAccount (split);
+ defer_bal_computation (gui, acc);
+
+ gnc_gen_trans_list_add_trans_with_ref_id (gui, trans, 0);
+ return;
+}/* end gnc_import_add_trans() */
+
void
gnc_gen_trans_list_add_trans_with_ref_id (GNCImportMainMatcher *gui, Transaction *trans, guint32 ref_id)
{
commit e116a4f195250b782c40e51a19a53409be73b480
Author: John Ralls <jralls at ceridwen.us>
Date: Tue Dec 13 12:32:54 2022 -0800
[import backend] Extract function hash_account_online_ids.
diff --git a/gnucash/import-export/import-backend.c b/gnucash/import-export/import-backend.c
index 14a1da107..b74333a16 100644
--- a/gnucash/import-export/import-backend.c
+++ b/gnucash/import-export/import-backend.c
@@ -1187,6 +1187,22 @@ static gint check_trans_online_id(Transaction *trans1, void *user_data)
return retval;
}
+static GHashTable*
+hash_account_online_ids (Account *account)
+{
+ GHashTable* acct_hash = g_hash_table_new_full
+ (g_str_hash, g_str_equal, g_free, NULL);
+ for (GList *n = xaccAccountGetSplitList (account) ; n; n = n->next)
+ {
+ if (gnc_import_split_has_online_id (n->data))
+ {
+ char *id = gnc_import_get_split_online_id (n->data);
+ g_hash_table_insert (acct_hash, (void*) id, GINT_TO_POINTER (1));
+ }
+ }
+ return acct_hash;
+}
+
/** Checks whether the given transaction's online_id already exists in
its parent account. */
gboolean gnc_import_exists_online_id (Transaction *trans, GHashTable* acct_id_hash)
@@ -1210,19 +1226,8 @@ gboolean gnc_import_exists_online_id (Transaction *trans, GHashTable* acct_id_ha
// test below will be fast if we have many transactions to import.
dest_acct = xaccSplitGetAccount (source_split);
if (!g_hash_table_contains (acct_id_hash, dest_acct))
- {
- GHashTable* new_hash = g_hash_table_new_full
- (g_str_hash, g_str_equal, g_free, NULL);
- g_hash_table_insert (acct_id_hash, dest_acct, new_hash);
- for (GList *n = xaccAccountGetSplitList (dest_acct) ; n; n=n->next)
- {
- if (gnc_import_split_has_online_id (n->data))
- {
- char *id = gnc_import_get_split_online_id (n->data);
- g_hash_table_insert (new_hash, (void*) id, GINT_TO_POINTER (1));
- }
- }
- }
+ g_hash_table_insert (acct_id_hash, dest_acct,
+ hash_account_online_ids (dest_acct));
online_id_exists = g_hash_table_contains (g_hash_table_lookup (acct_id_hash, dest_acct),
source_online_id);
Summary of changes:
gnucash/import-export/import-backend.c | 31 +-
gnucash/import-export/import-main-matcher.c | 31 +-
gnucash/import-export/ofx/gnc-ofx-import.c | 947 +++++++++++++++-------------
libgnucash/engine/Split.c | 2 +
libgnucash/engine/Transaction.c | 3 +
5 files changed, 545 insertions(+), 469 deletions(-)
More information about the gnucash-changes
mailing list