diff --git a/gnucash/gtkbuilder/dialog-import.glade b/gnucash/gtkbuilder/dialog-import.glade
index b32a5f842f..696a5fa605 100644
--- a/gnucash/gtkbuilder/dialog-import.glade
+++ b/gnucash/gtkbuilder/dialog-import.glade
@@ -960,7 +960,7 @@
+
+
+
+ False
+ True
+ 3
+
+
diff --git a/gnucash/import-export/import-backend.c b/gnucash/import-export/import-backend.c
index d9f86f6cce..c17487a227 100644
--- a/gnucash/import-export/import-backend.c
+++ b/gnucash/import-export/import-backend.c
@@ -95,6 +95,9 @@ struct _transactioninfo
/* Reference id to link gnc transaction to external object. E.g. aqbanking job id. */
guint32 ref_id;
+
+ /* When updating a matched transaction, append Description and Notes instead of replacing */
+ gboolean append_text;
};
/* Some simple getters and setters for the above data types. */
@@ -240,6 +243,15 @@ gnc_import_TransInfo_set_ref_id (GNCImportTransInfo *info,
}
+void
+gnc_import_TransInfo_set_append_text (GNCImportTransInfo *info,
+ gboolean append_text)
+{
+ g_assert (info);
+ info->append_text = append_text;
+}
+
+
Split *
gnc_import_MatchInfo_get_split (const GNCImportMatchInfo * info)
{
@@ -827,6 +839,77 @@ void split_find_match (GNCImportTransInfo * trans_info,
/***********************************************************************
*/
+/* append the imported transaction description to the matched transaction description */
+static void
+desc_append (Transaction* selected_match_trans, Transaction* imp_trans)
+{
+ gchar* tmp = g_strconcat( xaccTransGetDescription (selected_match_trans),
+ "|",
+ xaccTransGetDescription (imp_trans),
+ NULL);
+ xaccTransSetDescription (selected_match_trans, tmp);
+ g_free (tmp);
+}
+
+/* append the imported transaction notes to the matched transaction notes */
+static void
+notes_append (Transaction* selected_match_trans, Transaction* imp_trans)
+{
+ gchar* tmp = g_strconcat (xaccTransGetNotes (selected_match_trans),
+ "|",
+ xaccTransGetNotes (imp_trans),
+ NULL);
+ xaccTransSetNotes (selected_match_trans, tmp);
+ g_free (tmp);
+}
+
+/* Append or replace transaction description and notes
+ * depending on the Append checkbox
+ */
+static void
+update_desc_and_notes (const GNCImportTransInfo* trans_info)
+{
+ GNCImportMatchInfo* selected_match =
+ gnc_import_TransInfo_get_selected_match (trans_info);
+ Transaction* imp_trans = gnc_import_TransInfo_get_trans (trans_info);
+
+ if (trans_info->append_text)
+ {
+ gchar* desc_imported = g_utf8_normalize (xaccTransGetDescription (
+ imp_trans), -1, G_NORMALIZE_ALL);
+ gchar* desc_matched = g_utf8_normalize (xaccTransGetDescription (
+ selected_match->trans), -1, G_NORMALIZE_ALL);
+ gchar* note_imported = g_utf8_normalize (xaccTransGetNotes (
+ imp_trans), -1, G_NORMALIZE_ALL);
+ gchar* note_matched = g_utf8_normalize (xaccTransGetNotes (
+ selected_match->trans), -1, G_NORMALIZE_ALL);
+
+ // Append if desc_imported not already in desc_matched
+ if (g_utf8_strlen (desc_imported, -1) > g_utf8_strlen (desc_matched, -1) ||
+ !strstr (desc_matched, desc_imported))
+ desc_append (selected_match->trans, imp_trans);
+
+ // Append if note_imported not already in note_matched
+ if (g_utf8_strlen (note_imported, -1) > g_utf8_strlen (note_matched, -1) ||
+ !strstr (note_matched, note_imported))
+ notes_append (selected_match->trans, imp_trans);
+
+ g_free(desc_imported);
+ g_free(desc_matched);
+ g_free(note_imported);
+ g_free(note_matched);
+ }
+ else
+ {
+ // replace the matched transaction description with the imported transaction description
+ xaccTransSetDescription (selected_match->trans,
+ xaccTransGetDescription (imp_trans));
+ // replace the matched transaction notes with the imported transaction notes
+ xaccTransSetNotes (selected_match->trans,
+ xaccTransGetNotes (imp_trans));
+ }
+}
+
/** /brief -- Processes one match
according to its selected action. */
gboolean
@@ -944,13 +1027,7 @@ gnc_import_process_trans_item (GncImportMatchMap *matchmap,
to balance the transaction */
}
- xaccTransSetDescription(selected_match->trans,
- xaccTransGetDescription(
- gnc_import_TransInfo_get_trans(trans_info)));
-
- xaccTransSetNotes(selected_match->trans,
- xaccTransGetNotes(
- gnc_import_TransInfo_get_trans(trans_info)));
+ update_desc_and_notes( trans_info);
if (xaccSplitGetReconcile(selected_match->split) == NREC)
{
diff --git a/gnucash/import-export/import-backend.h b/gnucash/import-export/import-backend.h
index b524d18359..cb9c726631 100644
--- a/gnucash/import-export/import-backend.h
+++ b/gnucash/import-export/import-backend.h
@@ -238,6 +238,11 @@ void
gnc_import_TransInfo_set_ref_id (GNCImportTransInfo *info,
guint32 ref_id);
+/** Set the append_text for this TransInfo. */
+void
+gnc_import_TransInfo_set_append_text (GNCImportTransInfo *info,
+ gboolean append_text);
+
/**@}*/
/** @name Getters/Setters for GNCImportMatchInfo */
diff --git a/gnucash/import-export/import-main-matcher.c b/gnucash/import-export/import-main-matcher.c
index e6c9df3611..c0f11ae33d 100644
--- a/gnucash/import-export/import-main-matcher.c
+++ b/gnucash/import-export/import-main-matcher.c
@@ -72,6 +72,7 @@ struct _main_matcher_info
GtkTreeViewColumn *memo_column;
GtkWidget *show_account_column;
GtkWidget *show_matched_info;
+ GtkWidget *append_text; // Update+Clear: Append import Desc/Notes to matched Desc/Notes
GtkWidget *reconcile_after_close;
gboolean add_toggled; // flag to indicate that add has been toggled to stop selection
gint id;
@@ -444,7 +445,22 @@ resolve_conflicts (GNCImportMainMatcher *info)
void
gnc_gen_trans_list_show_all (GNCImportMainMatcher *info)
{
+ GNCImportTransInfo* trans_info;
+ Account* account;
+ Split* first_split;
+ GSList* temp_trans_list;
+
g_assert (info);
+
+ // Set initial state of Append checkbox to same as last import for this account.
+ // Get the import account from the first split in first transaction.
+ temp_trans_list = info->temp_trans_list;
+ trans_info = temp_trans_list->data;
+ first_split = gnc_import_TransInfo_get_fsplit (trans_info);
+ account = xaccSplitGetAccount(first_split);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (info->append_text),
+ xaccAccountGetAppendText(account));
+
gnc_gen_trans_list_create_matches (info);
resolve_conflicts (info);
gtk_widget_show_all (GTK_WIDGET(info->main_widget));
@@ -457,6 +473,9 @@ on_matcher_ok_clicked (GtkButton *button, GNCImportMainMatcher *info)
GtkTreeModel *model;
GtkTreeIter iter;
GNCImportTransInfo *trans_info;
+ gboolean append_text = gtk_toggle_button_get_active ((GtkToggleButton*) info->append_text);
+ gboolean first_tran = TRUE;
+ gpointer user_data = info->user_data;
g_assert (info);
@@ -479,6 +498,20 @@ on_matcher_ok_clicked (GtkButton *button, GNCImportMainMatcher *info)
DOWNLOADED_COL_DATA, &trans_info,
-1);
+ // Allow the backend to know if the Append checkbox is ticked or unticked
+ // by propagating info->append_text to every trans_info->append_text
+ gnc_import_TransInfo_set_append_text( trans_info, append_text);
+
+ // When processing the first transaction,
+ // save the state of the Append checkbox to an account kvp so the same state can be
+ // defaulted next time this account is imported.
+ // Get the import account from the first split.
+ if (first_tran)
+ {
+ Split* first_split = gnc_import_TransInfo_get_fsplit (trans_info);
+ xaccAccountSetAppendText (xaccSplitGetAccount(first_split), append_text);
+ first_tran = FALSE;
+ }
// Note: if there's only 1 split (unbalanced) one will be created with the unbalanced account,
// and for that account the defer balance will not be set. So things will be slow.
@@ -1222,7 +1255,9 @@ gnc_gen_trans_common_setup (GNCImportMainMatcher *info,
g_signal_connect (G_OBJECT(info->show_matched_info), "toggled",
G_CALLBACK(show_matched_info_toggled_cb), info);
- // Create the checkbox, but do not show it by default.
+ info->append_text = GTK_WIDGET(gtk_builder_get_object (builder, "append_desc_notes_button"));
+
+ // Create the checkbox, but do not show it unless there are transactions
info->reconcile_after_close = GTK_WIDGET(gtk_builder_get_object (builder, "reconcile_after_close_button"));
show_update = gnc_import_Settings_get_action_update_enabled (info->user_settings);
@@ -1905,6 +1940,13 @@ gnc_gen_trans_list_widget (GNCImportMainMatcher *info)
return info->main_widget;
}
+GtkWidget *
+gnc_gen_trans_list_append_text_widget (GNCImportMainMatcher *info)
+{
+ g_assert (info);
+ return info->append_text;
+}
+
gboolean
query_tooltip_tree_view_cb (GtkWidget *widget, gint x, gint y,
gboolean keyboard_tip,
diff --git a/gnucash/import-export/import-main-matcher.h b/gnucash/import-export/import-main-matcher.h
index 6b20c3d4f3..490d54c449 100644
--- a/gnucash/import-export/import-main-matcher.h
+++ b/gnucash/import-export/import-main-matcher.h
@@ -189,6 +189,13 @@ gboolean gnc_gen_trans_list_run (GNCImportMainMatcher *info);
*/
GtkWidget *gnc_gen_trans_list_widget (GNCImportMainMatcher *info);
+/** Returns the append_text widget of this dialog.
+ * @param info A pointer to a the GNCImportMainMatcher structure.
+ * @return A GtkWidget pointer to the append_text widget.
+ */
+GtkWidget *
+gnc_gen_trans_list_append_text_widget (GNCImportMainMatcher *info);
+
/** Checks whether there are no transactions to match.
* @param info A pointer to a the GNCImportMainMatcher structure.
* @return A boolean indicating whether the transaction list is empty.
diff --git a/libgnucash/engine/Account.cpp b/libgnucash/engine/Account.cpp
index 03e461d591..ee375d3046 100644
--- a/libgnucash/engine/Account.cpp
+++ b/libgnucash/engine/Account.cpp
@@ -65,6 +65,7 @@ static const std::string KEY_INCLUDE_CHILDREN("include-children");
static const std::string KEY_POSTPONE("postpone");
static const std::string KEY_LOT_MGMT("lot-mgmt");
static const std::string KEY_ONLINE_ID("online_id");
+static const std::string KEY_IMP_APPEND_TEXT("import-append-text");
static const std::string AB_KEY("hbci");
static const std::string AB_ACCOUNT_ID("account-id");
static const std::string AB_ACCOUNT_UID("account-uid");
@@ -116,6 +117,7 @@ enum
PROP_LOT_NEXT_ID, /* KVP */
PROP_ONLINE_ACCOUNT, /* KVP */
+ PROP_IMP_APPEND_TEXT, /* KVP */
PROP_IS_OPENING_BALANCE, /* KVP */
PROP_OFX_INCOME_ACCOUNT, /* KVP */
PROP_AB_ACCOUNT_ID, /* KVP */
@@ -484,6 +486,9 @@ gnc_account_get_property (GObject *object,
case PROP_ONLINE_ACCOUNT:
qof_instance_get_path_kvp (QOF_INSTANCE (account), value, {KEY_ONLINE_ID});
break;
+ case PROP_IMP_APPEND_TEXT:
+ g_value_set_boolean(value, xaccAccountGetAppendText(account));
+ break;
case PROP_OFX_INCOME_ACCOUNT:
qof_instance_get_path_kvp (QOF_INSTANCE (account), value, {KEY_ASSOC_INCOME_ACCOUNT});
break;
@@ -613,6 +618,9 @@ gnc_account_set_property (GObject *object,
case PROP_ONLINE_ACCOUNT:
qof_instance_set_path_kvp (QOF_INSTANCE (account), value, {KEY_ONLINE_ID});
break;
+ case PROP_IMP_APPEND_TEXT:
+ xaccAccountSetAppendText(account, g_value_get_boolean(value));
+ break;
case PROP_OFX_INCOME_ACCOUNT:
qof_instance_set_path_kvp (QOF_INSTANCE (account), value, {KEY_ASSOC_INCOME_ACCOUNT});
break;
@@ -1062,6 +1070,16 @@ gnc_account_class_init (AccountClass *klass)
NULL,
static_cast(G_PARAM_READWRITE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_IMP_APPEND_TEXT,
+ g_param_spec_boolean ("import-append-text",
+ "Import Append Text",
+ "Saved state of Append checkbox for setting initial "
+ "value next time this account is imported.",
+ FALSE,
+ static_cast(G_PARAM_READWRITE)));
+
g_object_class_install_property(
gobject_class,
PROP_OFX_INCOME_ACCOUNT,
@@ -4228,6 +4246,18 @@ xaccAccountSetPlaceholder (Account *acc, gboolean val)
set_boolean_key(acc, {"placeholder"}, val);
}
+gboolean
+xaccAccountGetAppendText (const Account *acc)
+{
+ return boolean_from_key(acc, {"import-append-text"});
+}
+
+void
+xaccAccountSetAppendText (Account *acc, gboolean val)
+{
+ set_boolean_key(acc, {"import-append-text"}, val);
+}
+
gboolean
xaccAccountGetIsOpeningBalance (const Account *acc)
{
diff --git a/libgnucash/engine/Account.h b/libgnucash/engine/Account.h
index 6822bfd655..19093036c1 100644
--- a/libgnucash/engine/Account.h
+++ b/libgnucash/engine/Account.h
@@ -1217,6 +1217,30 @@ gboolean xaccAccountGetPlaceholder (const Account *account);
* @param val The new state for the account's "placeholder" flag. */
void xaccAccountSetPlaceholder (Account *account, gboolean val);
+/** @name Account Append Text flag
+ @{
+*/
+
+/** Get the "import-append-text" flag for an account. This is the saved
+ * state of the Append checkbox in the "Generic import transaction matcher"
+ * used to set the initial state of the Append checkbox next time this
+ * account is imported.
+ *
+ * @param account The account whose flag should be retrieved.
+ *
+ * @return The current state of the account's "import-append-text" flag. */
+gboolean xaccAccountGetAppendText (const Account *account);
+
+/** Set the "import-append-text" flag for an account. This is the saved
+ * state of the Append checkbox in the "Generic import transaction matcher"
+ * used to set the initial state of the Append checkbox next time this
+ * account is imported.
+ *
+ * @param account The account whose flag should be retrieved.
+ *
+ * @param val The new state for the account's "import-append-text" flag. */
+void xaccAccountSetAppendText (Account *account, gboolean val);
+
/** Get the "opening-balance" flag for an account. If this flag is set
* then the account is used for opening balance transactions.
*