diff --git a/src/engine/Account.c b/src/engine/Account.c index d4b859f011..8331df354e 100644 --- a/src/engine/Account.c +++ b/src/engine/Account.c @@ -1529,7 +1529,297 @@ xaccAccountTypesCompatible (int parent_type, int child_type) /********************************************************************\ \********************************************************************/ +static kvp_frame * +xaccAccountGetReconcileInfo (Account *account) +{ + kvp_value *value; + if (!account) + return NULL; + + value = kvp_frame_get_slot (xaccAccountGetSlots (account), "reconcile-info"); + if (!value) + return NULL; + + return kvp_value_get_frame (value); +} + +/********************************************************************\ +\********************************************************************/ +static void +xaccAccountMakeReconcileInfo (Account *account) +{ + kvp_frame *slots; + + if (!account) + return; + + if (xaccAccountGetReconcileInfo (account)) + return; + + slots = xaccAccountGetSlots (account); + if (!slots) + return; + + xaccAccountBeginEdit (account); + { + kvp_frame *frame; + kvp_value *value; + + check_open (account); + + frame = kvp_frame_new (); + value = kvp_value_new_frame (frame); + + kvp_frame_set_slot (slots, "reconcile-info", value); + + kvp_value_delete (value); + kvp_frame_delete (frame); + + mark_account (account); + } + xaccAccountCommitEdit (account); +} + +/********************************************************************\ +\********************************************************************/ +gboolean +xaccAccountGetReconcileLastDate (Account *account, time_t *last_date) +{ + kvp_frame *recn_info; + kvp_value *value; + + if (!account) + return FALSE; + + recn_info = xaccAccountGetReconcileInfo (account); + if (!recn_info) + return FALSE; + + value = kvp_frame_get_slot (recn_info, "last-date"); + if (!value) + return FALSE; + + if (kvp_value_get_type (value) == KVP_TYPE_GINT64) + { + if (last_date) + *last_date = kvp_value_get_gint64 (value); + + return TRUE; + } + + return FALSE; +} + +/********************************************************************\ +\********************************************************************/ +void +xaccAccountSetReconcileLastDate (Account *account, time_t last_date) +{ + kvp_frame *recn_info; + + if (!account) + return; + + recn_info = xaccAccountGetReconcileInfo (account); + if (!recn_info) + { + xaccAccountMakeReconcileInfo (account); + recn_info = xaccAccountGetReconcileInfo (account); + if (!recn_info) + { + PERR ("Couldn't make reconcile info"); + return; + } + } + + xaccAccountBeginEdit (account); + { + kvp_value *value; + + check_open (account); + + value = kvp_value_new_gint64 (last_date); + + kvp_frame_set_slot (recn_info, "last-date", value); + + kvp_value_delete (value); + + mark_account (account); + } + xaccAccountCommitEdit (account); +} + +/********************************************************************\ +\********************************************************************/ +gboolean +xaccAccountGetReconcilePostponeDate (Account *account, + time_t *postpone_date) +{ + kvp_frame *recn_info; + kvp_value *value; + + if (!account) + return FALSE; + + recn_info = xaccAccountGetReconcileInfo (account); + if (!recn_info) + return FALSE; + + value = kvp_frame_get_slot (recn_info, "postpone-date"); + if (!value) + return FALSE; + + if (kvp_value_get_type (value) == KVP_TYPE_GINT64) + { + if (postpone_date) + *postpone_date = kvp_value_get_gint64 (value); + + return TRUE; + } + + return FALSE; +} + +/********************************************************************\ +\********************************************************************/ +void +xaccAccountSetReconcilePostponeDate (Account *account, + time_t postpone_date) +{ + kvp_frame *recn_info; + + if (!account) + return; + + recn_info = xaccAccountGetReconcileInfo (account); + if (!recn_info) + { + xaccAccountMakeReconcileInfo (account); + recn_info = xaccAccountGetReconcileInfo (account); + if (!recn_info) + { + PERR ("Couldn't make reconcile info"); + return; + } + } + + xaccAccountBeginEdit (account); + { + kvp_value *value; + + check_open (account); + + value = kvp_value_new_gint64 (postpone_date); + + kvp_frame_set_slot (recn_info, "postpone-date", value); + + kvp_value_delete (value); + + mark_account (account); + } + xaccAccountCommitEdit (account); +} + +/********************************************************************\ +\********************************************************************/ +gboolean +xaccAccountGetReconcilePostponeBalance (Account *account, + gnc_numeric *balance) +{ + kvp_frame *recn_info; + kvp_value *value; + + if (!account) + return FALSE; + + recn_info = xaccAccountGetReconcileInfo (account); + if (!recn_info) + return FALSE; + + value = kvp_frame_get_slot (recn_info, "postpone-balance"); + if (!value) + return FALSE; + + if (kvp_value_get_type (value) == KVP_TYPE_GINT64) + { + if (balance) + *balance = kvp_value_get_numeric (value); + + return TRUE; + } + + return FALSE; +} + +/********************************************************************\ +\********************************************************************/ +void +xaccAccountSetReconcilePostponeBalance (Account *account, + gnc_numeric balance) +{ + kvp_frame *recn_info; + + if (!account) + return; + + recn_info = xaccAccountGetReconcileInfo (account); + if (!recn_info) + { + xaccAccountMakeReconcileInfo (account); + recn_info = xaccAccountGetReconcileInfo (account); + if (!recn_info) + { + PERR ("Couldn't make reconcile info"); + return; + } + } + + xaccAccountBeginEdit (account); + { + kvp_value *value; + + check_open (account); + + value = kvp_value_new_numeric (balance); + + kvp_frame_set_slot (recn_info, "postpone-balance", value); + + kvp_value_delete (value); + + mark_account (account); + } + xaccAccountCommitEdit (account); +} + +/********************************************************************\ +\********************************************************************/ +void +xaccAccountClearReconcilePostpone (Account *account) +{ + kvp_frame *recn_info; + + if (!account) + return; + + recn_info = xaccAccountGetReconcileInfo (account); + if (!recn_info) + return; + + xaccAccountBeginEdit (account); + { + check_open (account); + + kvp_frame_set_slot (recn_info, "postpone-date", NULL); + kvp_frame_set_slot (recn_info, "postpone-balance", NULL); + + mark_account (account); + } + xaccAccountCommitEdit (account); +} + +/********************************************************************\ +\********************************************************************/ void xaccAccountSetPriceSrc(Account *acc, const char *src) { diff --git a/src/engine/Account.h b/src/engine/Account.h index 334ceeda07..7ce8df0b3f 100644 --- a/src/engine/Account.h +++ b/src/engine/Account.h @@ -277,6 +277,25 @@ void xaccClearMark (Account *account, short val); void xaccClearMarkDown (Account *account, short val); void xaccClearMarkDownGr (AccountGroup *group, short val); +/* The following functions get and set reconciliation information */ +gboolean xaccAccountGetReconcileLastDate (Account *account, + time_t *last_date); +void xaccAccountSetReconcileLastDate (Account *account, + time_t last_date); + +gboolean xaccAccountGetReconcilePostponeDate (Account *account, + time_t *postpone_date); +void xaccAccountSetReconcilePostponeDate (Account *account, + time_t postpone_date); + +gboolean xaccAccountGetReconcilePostponeBalance (Account *account, + gnc_numeric *balance); +void xaccAccountSetReconcilePostponeBalance (Account *account, + gnc_numeric balance); + +void xaccAccountClearReconcilePostpone (Account *account); + + /* The xaccAccountSetPriceSrc() and xaccAccountGetPriceSrc() routines are used to get and set a string that identifies the current source for investment pricing info. Currently supported values include diff --git a/src/engine/kvp_doc.txt b/src/engine/kvp_doc.txt index 156bd309d9..9ae81d9fda 100644 --- a/src/engine/kvp_doc.txt +++ b/src/engine/kvp_doc.txt @@ -66,6 +66,26 @@ Use: This string holds the old Price Source code used by earlier versions is fully supported. The new version of Finance::Quote uses a different scheme to identify sources for price quotes. +Name: reconcile-info +Type: frame +Entities: Account +Use: store reconcile information about accounts + +Name: reconcile-info/last-date +Type: frame +Entities: Account +Use: store the statement date of the last reconciliation + +Name: reconcile-info/postpone-date +Type: gint64 +Entities: Account +Use: store the ending statement date of a postponed reconciliation + +Name: reconcile-info/postpone-balance +Type: numeric +Entities: Account +Use: store the ending balance of a postponed reconciliation + Name: tax-related Type: gint64 Entities: Account diff --git a/src/engine/kvp_frame.h b/src/engine/kvp_frame.h index 1569a1bcb2..e76895cc5d 100644 --- a/src/engine/kvp_frame.h +++ b/src/engine/kvp_frame.h @@ -65,7 +65,7 @@ kvp_frame * kvp_frame_new(void); void kvp_frame_delete(kvp_frame * frame); kvp_frame * kvp_frame_copy(const kvp_frame * frame); void kvp_frame_set_slot(kvp_frame * frame, - const char * key, const kvp_value * value); + const char * key, const kvp_value * value); kvp_value * kvp_frame_get_slot(kvp_frame * frame, const char * key); diff --git a/src/gnome/reconcile-list.c b/src/gnome/reconcile-list.c index 1a79db78c8..a1c573d400 100644 --- a/src/gnome/reconcile-list.c +++ b/src/gnome/reconcile-list.c @@ -146,6 +146,7 @@ gnc_reconcile_list_init(GNCReconcileList *list) list->current_split = NULL; list->no_toggle = FALSE; list->always_unselect = FALSE; + list->first_fill = TRUE; list->query = NULL; while (titles[list->num_columns] != NULL) @@ -263,12 +264,11 @@ gnc_reconcile_list_set_row_style(GNCReconcileList *list, gint row, } static void -gnc_reconcile_list_toggle(GNCReconcileList *list) +gnc_reconcile_list_toggle (GNCReconcileList *list) { Split *split, *current; gboolean reconciled; const char *recn_str; - char recn; gint row; g_assert(IS_GNC_RECONCILE_LIST(list)); @@ -294,9 +294,8 @@ gnc_reconcile_list_toggle(GNCReconcileList *list) g_hash_table_remove(list->reconciled, split); } - recn = xaccSplitGetReconcile(split); - recn = reconciled ? YREC : recn; - recn_str = gnc_get_reconcile_str(recn); + recn_str = reconciled ? gnc_get_reconcile_str (YREC) : ""; + gtk_clist_set_text(GTK_CLIST(list), row, 4, recn_str); gnc_reconcile_list_set_row_style(list, row, reconciled); @@ -533,7 +532,7 @@ gnc_reconcile_list_reconciled_balance(GNCReconcileList *list) * Returns: nothing * \********************************************************************/ void -gnc_reconcile_list_commit(GNCReconcileList *list, time_t date) +gnc_reconcile_list_commit (GNCReconcileList *list, time_t date) { GtkCList *clist = GTK_CLIST(list); Split *split; @@ -547,13 +546,49 @@ gnc_reconcile_list_commit(GNCReconcileList *list, time_t date) for (i = 0; i < list->num_splits; i++) { - split = gtk_clist_get_row_data(clist, i); + char recn; - if (g_hash_table_lookup(list->reconciled, split) != NULL) - { - xaccSplitSetReconcile(split, YREC); - xaccSplitSetDateReconciledSecs(split, date); - } + split = gtk_clist_get_row_data (clist, i); + + recn = g_hash_table_lookup (list->reconciled, split) ? YREC : NREC; + + xaccSplitSetReconcile (split, recn); + if (recn == YREC) + xaccSplitSetDateReconciledSecs (split, date); + } +} + + +/********************************************************************\ + * gnc_reconcile_list_commit * + * postpone the reconcile information in the list by setting * + * reconciled splits to cleared status * + * * + * Args: list - list to commit * + * Returns: nothing * +\********************************************************************/ +void +gnc_reconcile_list_postpone (GNCReconcileList *list) +{ + GtkCList *clist = GTK_CLIST(list); + Split *split; + int i; + + g_return_if_fail(list != NULL); + g_return_if_fail(IS_GNC_RECONCILE_LIST(list)); + + if (list->reconciled == NULL) + return; + + for (i = 0; i < list->num_splits; i++) + { + char recn; + + split = gtk_clist_get_row_data (clist, i); + + recn = g_hash_table_lookup (list->reconciled, split) ? CREC : NREC; + + xaccSplitSetReconcile (split, recn); } } @@ -678,12 +713,11 @@ gnc_reconcile_list_fill(GNCReconcileList *list) strings[2] = xaccTransGetDescription(trans); strings[3] = xaccPrintAmount(gnc_numeric_abs (amount), print_info); + if (list->first_fill && recn == CREC) + g_hash_table_insert (list->reconciled, split, split); + reconciled = g_hash_table_lookup(list->reconciled, split) != NULL; - recn = reconciled ? YREC : recn; - if (recn == NREC) - strings[4] = ""; - else - strings[4] = gnc_get_reconcile_str(recn); + strings[4] = reconciled ? gnc_get_reconcile_str (YREC) : ""; row = gtk_clist_append(GTK_CLIST(list), (gchar **) strings); gtk_clist_set_row_data(GTK_CLIST(list), row, split); @@ -692,4 +726,6 @@ gnc_reconcile_list_fill(GNCReconcileList *list) list->num_splits++; } + + list->first_fill = FALSE; } diff --git a/src/gnome/reconcile-list.h b/src/gnome/reconcile-list.h index c6102f01c0..60fa861b6f 100644 --- a/src/gnome/reconcile-list.h +++ b/src/gnome/reconcile-list.h @@ -61,6 +61,7 @@ struct _GNCReconcileList gboolean no_toggle; gboolean always_unselect; + gboolean first_fill; GHashTable *reconciled; @@ -100,7 +101,9 @@ void gnc_reconcile_list_refresh (GNCReconcileList *list); gnc_numeric gnc_reconcile_list_reconciled_balance(GNCReconcileList *list); -void gnc_reconcile_list_commit(GNCReconcileList *list, time_t date); +void gnc_reconcile_list_commit (GNCReconcileList *list, time_t date); + +void gnc_reconcile_list_postpone (GNCReconcileList *list); void gnc_reconcile_list_unselect_all(GNCReconcileList *list); diff --git a/src/gnome/window-reconcile.c b/src/gnome/window-reconcile.c index c2ec8de675..949c072f0f 100644 --- a/src/gnome/window-reconcile.c +++ b/src/gnome/window-reconcile.c @@ -32,6 +32,7 @@ #include #include +#include #include "AccWindow.h" #include "MainWindow.h" @@ -323,13 +324,13 @@ startRecnWindow(GtkWidget *parent, Account *account, } /* Create the dialog box */ - title = gnc_recn_make_window_name(account); + title = gnc_recn_make_window_name (account); - dialog = gnome_dialog_new(title, - GNOME_STOCK_BUTTON_OK, - GNOME_STOCK_BUTTON_CANCEL, - NULL); - g_free(title); + dialog = gnome_dialog_new (title, + GNOME_STOCK_BUTTON_OK, + GNOME_STOCK_BUTTON_CANCEL, + NULL); + g_free (title); gnome_dialog_set_default(GNOME_DIALOG(dialog), 0); gnome_dialog_set_close(GNOME_DIALOG(dialog), TRUE); @@ -1273,6 +1274,28 @@ gnc_recn_create_tool_bar(RecnWindow *recnData) return toolbar; } +static void +gnc_get_reconcile_info (Account *account, + gnc_numeric *new_ending, + time_t *statement_date) +{ + if (xaccAccountGetReconcileLastDate (account, statement_date)) + { + struct tm *tm; + + tm = localtime (statement_date); + + tm->tm_mon++; + tm->tm_isdst = -1; + + *statement_date = mktime (tm); + } + + xaccAccountGetReconcilePostponeDate (account, statement_date); + + xaccAccountGetReconcilePostponeBalance (account, new_ending); +} + static gboolean find_by_account (gpointer find_data, gpointer user_data) { @@ -1372,19 +1395,20 @@ recnWindow (GtkWidget *parent, Account *account) new_ending = xaccAccountGetBalance(account); /* The last time reconciliation was attempted during the current - * execution of gnucash, the date was stored. Use that date if - * possible. This helps with balancing multiple accounts for - * which statements are issued at the same time, like multiple - * bank accounts on a single statement. - */ + * execution of gnucash, the date was stored. Use that date if + * possible. This helps with balancing multiple accounts for which + * statements are issued at the same time, like multiple bank + * accounts on a single statement. */ if (!last_statement_date) - statement_date = time(NULL); + statement_date = time (NULL); else statement_date = last_statement_date; + gnc_get_reconcile_info (account, &new_ending, &statement_date); + /* Popup a little window to prompt the user to enter the * ending balance for his/her bank statement */ - if (!startRecnWindow(parent, account, &new_ending, &statement_date)) + if (!startRecnWindow (parent, account, &new_ending, &statement_date)) { gnc_unregister_gui_component_by_data (WINDOW_RECONCILE_CM_CLASS, recnData); g_free (recnData); @@ -1750,6 +1774,9 @@ recnFinishCB (GtkWidget *w, gpointer data) account = recn_get_account (recnData); + xaccAccountClearReconcilePostpone (account); + xaccAccountSetReconcileLastDate (account, date); + if (auto_payment && (xaccAccountGetType (account) == CREDIT) && (gnc_numeric_negative_p (recnData->new_ending)))