diff --git a/AUTHORS b/AUTHORS index 97daacdf3e..752a6d1913 100644 --- a/AUTHORS +++ b/AUTHORS @@ -84,6 +84,7 @@ Terry Boldt financial calculator and expression parser Richard Braakman patch to RPM spec Christopher B. Browne for perl, lots of scheme and documentation updates +Paul Campbell reconcile children patch Conrad Canterford register bug fix Bill Carlson performance improvements David Mar?n Carre?o Spanish translation of account setup. diff --git a/doc/sgml/C/xacc-about.sgml b/doc/sgml/C/xacc-about.sgml index 351f90c7b6..ee5e057532 100644 --- a/doc/sgml/C/xacc-about.sgml +++ b/doc/sgml/C/xacc-about.sgml @@ -267,6 +267,14 @@ linkend="scheme"> Scheme , the g-wrap + +kemitix@users.sourceforge.net Paul + Campbell + +reconcile children patch + + + conrad@mail.watersprite.com.au Conrad Canterford diff --git a/src/app-utils/gnc-ui-util.c b/src/app-utils/gnc-ui-util.c index 3a5fc150fb..54efd1ea59 100644 --- a/src/app-utils/gnc-ui-util.c +++ b/src/app-utils/gnc-ui-util.c @@ -311,6 +311,54 @@ gnc_account_get_balance_in_currency (Account *account, } +gnc_numeric +gnc_ui_convert_balance_to_currency(gnc_numeric balance, gnc_commodity *balance_currency, gnc_commodity *currency) +{ + GNCBook *book; + GNCPriceDB *pdb; + GNCPrice *price; + + if (gnc_numeric_zero_p (balance) || + gnc_commodity_equiv (currency, balance_currency)) + return balance; + + book = gnc_get_current_book (); + pdb = gnc_book_get_pricedb (book); + + price = gnc_pricedb_lookup_latest (pdb, balance_currency, currency); + if (!price) + return gnc_numeric_zero (); + + balance = gnc_numeric_mul (balance, gnc_price_get_value (price), + gnc_commodity_get_fraction (currency), + GNC_RND_ROUND); + + gnc_price_unref (price); + + return balance; +} + +static gnc_numeric +gnc_account_get_reconciled_balance_in_currency (Account *account, + gnc_commodity *currency) +{ + GNCBook *book; + GNCPriceDB *pdb; + GNCPrice *price; + gboolean has_shares; + gnc_numeric balance; + GNCAccountType atype; + gnc_commodity *balance_currency; + + if (!account || !currency) + return gnc_numeric_zero (); + + balance = xaccAccountGetReconciledBalance (account); + balance_currency = DxaccAccountGetCurrency (account); + + return gnc_ui_convert_balance_to_currency (balance, balance_currency, currency); +} + typedef struct { gnc_commodity *currency; @@ -336,6 +384,21 @@ balance_helper (Account *account, gpointer data) return NULL; } +static gpointer +reconciled_balance_helper (Account *account, gpointer data) +{ + CurrencyBalance *cb = data; + gnc_numeric balance; + + balance = gnc_account_get_reconciled_balance_in_currency (account, cb->currency); + + cb->balance = gnc_numeric_add (cb->balance, balance, + gnc_commodity_get_fraction (cb->currency), + GNC_RND_ROUND); + + return NULL; +} + gnc_numeric gnc_ui_account_get_balance (Account *account, gboolean include_children) { @@ -450,6 +513,82 @@ gnc_ui_account_get_tax_info_string (Account *account) } +gnc_numeric +gnc_ui_account_get_reconciled_balance (Account *account, + gboolean include_children) +{ + gnc_numeric balance; + gnc_commodity *currency; + + if (account == NULL) + return gnc_numeric_zero (); + + currency = DxaccAccountGetCurrency (account); + + balance = gnc_account_get_reconciled_balance_in_currency (account, currency); + + if (include_children) + { + AccountGroup *children; + CurrencyBalance cb = { currency, balance }; + + children = xaccAccountGetChildren (account); + + xaccGroupForEachAccount (children, reconciled_balance_helper, &cb, TRUE); + + balance = cb.balance; + } + + /* reverse sign if needed */ + if (gnc_reverse_balance (account)) + balance = gnc_numeric_neg (balance); + + return balance; +} + +gnc_numeric +gnc_ui_account_get_balance_as_of_date (Account *account, time_t date, + gboolean include_children) +{ + gnc_numeric balance; + gnc_commodity *currency; + + if (account == NULL) + return gnc_numeric_zero (); + + currency = DxaccAccountGetCurrency (account); + balance = xaccAccountGetBalanceAsOfDate (account, date); + + if (include_children) + { + AccountGroup *children_group; + GList *children, *node; + + children_group = xaccAccountGetChildren (account); + children = xaccGroupGetSubAccounts (children_group); + + for( node = children; node; node = node->next ) + { + Account *child; + gnc_commodity *child_currency; + gnc_numeric child_balance; + + child = (Account *)node->data; + child_currency = DxaccAccountGetCurrency (child); + child_balance = xaccAccountGetBalanceAsOfDate (child, date); + child_balance = gnc_ui_convert_balance_to_currency + (child_balance, child_currency, currency); + balance = gnc_numeric_add_fixed (balance, child_balance); + } + } + + /* reverse sign if needed */ + if (gnc_reverse_balance (account)) + balance = gnc_numeric_neg (balance); + + return balance; +} + char * gnc_ui_account_get_field_value_string (Account *account, AccountFieldCode field) diff --git a/src/app-utils/gnc-ui-util.h b/src/app-utils/gnc-ui-util.h index f845ed4ef3..134211abb7 100644 --- a/src/app-utils/gnc-ui-util.h +++ b/src/app-utils/gnc-ui-util.h @@ -73,9 +73,16 @@ const char * gnc_ui_account_get_field_name (AccountFieldCode field); char * gnc_ui_account_get_field_value_string (Account *account, AccountFieldCode field); +gnc_numeric gnc_ui_convert_balance_to_currency(gnc_numeric balance, + gnc_commodity *balance_currency, + gnc_commodity *currency); + gnc_numeric gnc_ui_account_get_balance (Account *account, gboolean include_children); +gnc_numeric gnc_ui_account_get_reconciled_balance(Account *account, gboolean include_children); +gnc_numeric gnc_ui_account_get_balance_as_of_date (Account *account, time_t date, gboolean include_children); + const char * gnc_get_reconcile_str (char reconciled_flag); typedef enum diff --git a/src/engine/Account.c b/src/engine/Account.c index 83737dd2c2..613fd3e88f 100644 --- a/src/engine/Account.c +++ b/src/engine/Account.c @@ -2054,6 +2054,48 @@ xaccAccountGetQuoteTZ(Account *acc) return NULL; } +/********************************************************************\ +\********************************************************************/ +void +xaccAccountSetReconcileChildrenStatus(Account *account, gboolean status) +{ + kvp_frame *frame; + if (!account) + return; + + xaccAccountBeginEdit (account); + + frame = kvp_frame_get_frame (account->kvp_data, "reconcile-info", NULL); + kvp_frame_set_slot_nc (frame, + "include-children", + status ? kvp_value_new_gint64 (status) : NULL); + account->core_dirty = TRUE; + xaccAccountCommitEdit (account); + return; +} + +/********************************************************************\ +\********************************************************************/ + +gboolean +xaccAccountGetReconcileChildrenStatus(Account *account) +{ + kvp_value *status; + if (!account) + return FALSE; + /* access the account's kvp-data for status and return that, if no value + * is found then we can assume not to include the children, that being + * the default behaviour + */ + status = kvp_frame_get_slot_path (account->kvp_data, + "reconcile-info", + "include-children", + NULL); + if (!status) + return FALSE; + return kvp_value_get_gint64 (status); +} + /********************************************************************\ \********************************************************************/ diff --git a/src/engine/Account.h b/src/engine/Account.h index 1942fc621b..bfecccb6dc 100644 --- a/src/engine/Account.h +++ b/src/engine/Account.h @@ -234,6 +234,9 @@ gnc_numeric xaccAccountGetBalance (Account *account); gnc_numeric xaccAccountGetClearedBalance (Account *account); gnc_numeric xaccAccountGetReconciledBalance (Account *account); +void xaccAccountSetReconcileChildrenStatus(Account *account, gboolean status); +gboolean xaccAccountGetReconcileChildrenStatus(Account *account); + gnc_numeric xaccAccountGetBalanceAsOfDate (Account *account, time_t date); GList* xaccAccountGetSplitList (Account *account); diff --git a/src/engine/kvp_doc.txt b/src/engine/kvp_doc.txt index e1e69f9559..d3e92a1cea 100644 --- a/src/engine/kvp_doc.txt +++ b/src/engine/kvp_doc.txt @@ -102,6 +102,12 @@ Type: frame Entities: Account Use: store reconcile information about accounts +Name: reconcile-info/include-children +Type: gint64 +Entities: Account +Use: A boolean flag indicating whether transactions in sub-accounts should be + included during reconcilition. + Name: reconcile-info/last-date Type: frame Entities: Account diff --git a/src/gnome/reconcile-list.c b/src/gnome/reconcile-list.c index 3ee5935f6b..7b0f657ffe 100644 --- a/src/gnome/reconcile-list.c +++ b/src/gnome/reconcile-list.c @@ -98,6 +98,7 @@ GtkWidget * gnc_reconcile_list_new(Account *account, GNCReconcileListType type) { GNCReconcileList *list; + gboolean include_children; g_assert(account != NULL); g_assert((type == RECLIST_DEBIT) || (type == RECLIST_CREDIT)); @@ -114,6 +115,20 @@ gnc_reconcile_list_new(Account *account, GNCReconcileListType type) /* match the account */ xaccQueryAddSingleAccountMatch(list->query, account, QUERY_OR); + include_children = xaccAccountGetReconcileChildrenStatus(account); + if(include_children) + { + /* match child accounts */ + AccountGroup *account_group = xaccAccountGetChildren(account); + GList *children_list = xaccGroupGetSubAccounts(account_group); + GList *node; + + for (node = children_list; node; node = node->next) + xaccQueryAddSingleAccountMatch (list->query, node->data, QUERY_OR); + + g_list_free (children_list); + } + if (type == RECLIST_CREDIT) DxaccQueryAddAmountMatch(list->query, 0.0, AMT_SGN_MATCH_CREDIT, AMT_MATCH_ATLEAST, QUERY_AND); @@ -241,11 +256,87 @@ gnc_reconcile_list_class_init (GNCReconcileListClass *klass) klass->double_click_split = NULL; } +static void +gnc_reconcile_list_toggle_row(GNCReconcileList *list, gint row) +{ + Split *split, *current; + + g_assert (IS_GNC_RECONCILE_LIST(list)); + g_assert (list->reconciled != NULL); + + if (list->no_toggle) + return; + + split = gtk_clist_get_row_data (GTK_CLIST(list), row); + current = g_hash_table_lookup (list->reconciled, split); + + list->current_split = split; + + if (current == NULL) + g_hash_table_insert (list->reconciled, split, split); + else + g_hash_table_remove (list->reconciled, split); + + update_toggle (GTK_CLIST (list), row); +} + +static void +gnc_reconcile_list_toggle_children(Account *account, GNCReconcileList *list, Split *split) +{ + AccountGroup *account_group; + GList *child_accounts, *node; + Transaction *transaction; + + /* + * Need to get all splits in this transaction and identify any that are + * in the same heirarchy as the account being reconciled (not necessarily + * the account this split is from.) + * + * For each of these splits toggle them all to the same state. + */ + account_group = xaccAccountGetChildren(account); + child_accounts = xaccGroupGetSubAccounts(account_group); + child_accounts = g_list_prepend(child_accounts, account); + transaction = xaccSplitGetParent(split); + for(node = xaccTransGetSplitList(transaction); node; node = node->next) + { + Split *other_split; + Account *other_account; + GNCReconcileList *current_list; + gint row; + GtkCList *split_list; + + other_split = node->data; + other_account = xaccSplitGetAccount(other_split); + if(other_split == split) + continue; + /* Check this 'other' account in in the same heirarchy */ + if(!g_list_find(child_accounts,other_account)) + continue; + /* Search our sibling list for this split first. We search the + * sibling list first because that it where it is most likely to be. + */ + current_list = list->sibling; + row = gtk_clist_find_row_from_data(GTK_CLIST(¤t_list->clist), other_split); + if(row == -1) { + /* Not in the sibling list, try this list */ + current_list = list; + row = gtk_clist_find_row_from_data(GTK_CLIST(¤t_list->clist), other_split); + if(row == -1) + /* We can't find it, nothing more I can do about it */ + continue; + } + gnc_reconcile_list_toggle_row(current_list, row); + } +} + static void gnc_reconcile_list_toggle (GNCReconcileList *list) { Split *split, *current; gint row; + Account * account; + gboolean include_children; g_assert (IS_GNC_RECONCILE_LIST(list)); g_assert (list->reconciled != NULL); @@ -266,6 +357,10 @@ gnc_reconcile_list_toggle (GNCReconcileList *list) update_toggle (GTK_CLIST (list), row); + include_children = xaccAccountGetReconcileChildrenStatus(list->account); + if(include_children) + gnc_reconcile_list_toggle_children(list->account, list, split); + gtk_signal_emit (GTK_OBJECT (list), reconcile_list_signals[TOGGLE_RECONCILED], split); } diff --git a/src/gnome/reconcile-list.h b/src/gnome/reconcile-list.h index 8075e60835..59be4f230c 100644 --- a/src/gnome/reconcile-list.h +++ b/src/gnome/reconcile-list.h @@ -67,6 +67,8 @@ struct _GNCReconcileList Account *account; Query *query; + + GNCReconcileList *sibling; }; struct _GNCReconcileListClass diff --git a/src/gnome/window-reconcile.c b/src/gnome/window-reconcile.c index 5022431f6d..fa849fcae1 100644 --- a/src/gnome/window-reconcile.c +++ b/src/gnome/window-reconcile.c @@ -224,7 +224,7 @@ recnRecalculateBalance (RecnWindow *recnData) gnc_numeric reconciled; gnc_numeric diff; GNCPrintAmountInfo print_info; - gboolean reverse_balance; + gboolean reverse_balance, include_children; account = recn_get_account (recnData); if (!account) @@ -233,7 +233,8 @@ recnRecalculateBalance (RecnWindow *recnData) reverse_balance = gnc_reverse_balance(account); /* update the starting balance */ - starting = xaccAccountGetReconciledBalance(account); + include_children = xaccAccountGetReconcileChildrenStatus(account); + starting = gnc_ui_account_get_reconciled_balance(account, include_children); print_info = gnc_account_print_info (account, TRUE); if (reverse_balance) @@ -553,13 +554,14 @@ static gboolean startRecnWindow(GtkWidget *parent, Account *account, gnc_numeric *new_ending, time_t *statement_date) { - GtkWidget *dialog, *end_value, *date_value; + GtkWidget *dialog, *end_value, *date_value, *include_children_button; startRecnWindowData data = { NULL }; gboolean auto_interest_xfer_option; GNCPrintAmountInfo print_info; gnc_numeric ending; char *title; int result; + gboolean include_children; /* Initialize the data structure that will be used for several callbacks * throughout this file with the relevant info. Some initialization is @@ -575,7 +577,10 @@ startRecnWindow(GtkWidget *parent, Account *account, auto_interest_xfer_option = gnc_recn_interest_xfer_get_auto_interest_xfer_allowed( account ); - ending = xaccAccountGetReconciledBalance(account); + include_children = xaccAccountGetReconcileChildrenStatus(account); + + ending = gnc_ui_account_get_reconciled_balance(account, + include_children); print_info = gnc_account_print_info (account, TRUE); if (gnc_reverse_balance(account)) @@ -610,10 +615,16 @@ startRecnWindow(GtkWidget *parent, Account *account, GtkWidget *end_title = gtk_label_new(_("Ending Balance:")); GtkWidget *start_value = gtk_label_new(xaccPrintAmount (ending, print_info)); + GtkWidget *blank_label = gtk_label_new(""); GtkWidget *vbox = GNOME_DIALOG(dialog)->vbox; GtkWidget *entry; GtkWidget *interest = NULL; + include_children_button = + gtk_check_button_new_with_label(_("Include Subaccounts")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(include_children_button), + include_children); + date_value = gnc_date_edit_new(*statement_date, FALSE, FALSE); end_value = gnc_amount_edit_new (); @@ -655,10 +666,12 @@ startRecnWindow(GtkWidget *parent, Account *account, gtk_box_pack_start(GTK_BOX(left_column), date_title, TRUE, TRUE, 3); gtk_box_pack_start(GTK_BOX(left_column), start_title, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(left_column), end_title, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(left_column), blank_label, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(right_column), date_value, TRUE, TRUE, 3); gtk_box_pack_start(GTK_BOX(right_column), start_value, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(right_column), end_value, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(right_column), include_children_button, TRUE, TRUE, 0); /* if it's possible to enter an interest payment or charge for this * account, add a button so that the user can pop up the appropriate @@ -707,6 +720,9 @@ startRecnWindow(GtkWidget *parent, Account *account, if (gnc_reverse_balance(account)) *new_ending = gnc_numeric_neg (*new_ending); + + xaccAccountSetReconcileChildrenStatus + (account, GTK_TOGGLE_BUTTON(include_children_button)->active); } /* cancel or delete */ @@ -1604,7 +1620,10 @@ gnc_get_reconcile_info (Account *account, /* if the account wasn't previously postponed, try to predict * the statement balance based on the statement date. */ - *new_ending = xaccAccountGetBalanceAsOfDate(account, *statement_date); + *new_ending = + gnc_ui_account_get_balance_as_of_date + (account, *statement_date, + xaccAccountGetReconcileChildrenStatus(account)); } } @@ -1844,6 +1863,9 @@ recnWindow (GtkWidget *parent, Account *account) (account, RECLIST_CREDIT, recnData, &recnData->credit, &recnData->total_credit); + GNC_RECONCILE_LIST(recnData->debit)->sibling = GNC_RECONCILE_LIST(recnData->credit); + GNC_RECONCILE_LIST(recnData->credit)->sibling = GNC_RECONCILE_LIST(recnData->debit); + popup = gnc_recn_create_popup_menu(recnData); gnome_popup_menu_attach(popup, recnData->debit, recnData); gnome_popup_menu_attach(popup, recnData->credit, recnData);