Bug797678 OFX import should append not replace, existing Notes & Desc

Add an "Append" checkbox to the bottom of the "Generic import
transaction matcher" window to the left of the "Reconcile after match"
checkbox.
When ticked, this causes the imported Description/Notes to be appended
to the matched transaction Description/Notes respectively.
The selected ticked/unticked state of the "Append" checkbox is saved in
a key value pair for the import account, so the next import for that
account will automatically default it to the saved state.
As these mods are limited to the code for the matcher window, this
should work for all the imports that use it - ie ofx & csv file imports
(both tested) & aqbanking (cannot test).
pull/1157/head
goodvibes2 5 years ago
parent 285017793d
commit 8ad29feaba

@ -960,7 +960,7 @@
</child>
<child>
<object class="GtkCheckButton" id="show_matched_info_button">
<property name="label" translatable="yes">Show _matched information</property>
<property name="label" translatable="yes">Show matched _information</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
@ -973,9 +973,25 @@
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="append_desc_notes_button">
<property name="label" translatable="yes">A_ppend</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">When Updating and Clearing a matched transaction, append the imported Description and Notes to the matched Description and Notes instead of replacing them.</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="reconcile_after_close_button">
<property name="label" translatable="yes">Reconcile after match</property>
<property name="label" translatable="yes">_Reconcile after match</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="no_show_all">True</property>
@ -985,7 +1001,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
<property name="position">4</property>
</packing>
</child>
</object>

@ -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)
{

@ -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 */

@ -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,

@ -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.

@ -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<GParamFlags>(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<GParamFlags>(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)
{

@ -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.
*

Loading…
Cancel
Save