diff --git a/AUTHORS b/AUTHORS index cfabd6da7f..04afc091c0 100644 --- a/AUTHORS +++ b/AUTHORS @@ -100,7 +100,7 @@ Paul Fenwick ASX support, Finance::Quote Hubert Figuiere patch to gnc-prices Valek Filippov messages Russian translation Jan-Uwe Finck for messages German translation -Kevin Finn auto-decimal point patch, options patch +Kevin Finn auto-decimal point, options, auto interest xfer patches Ron Forrester for gnome patches Dave Freese for leap-year fix John Goerzen file i/o fix for 64-bit architectures diff --git a/ChangeLog b/ChangeLog index 0a8456c53d..7f0f65403e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +2001-06-17 Kevin Finn + + * src/gnome/window-reconcile.c: Implemented automatic interest + transfer as a stripped-down transfer dialog. This allows the user + to automatically enter an interest charge or payment for bank or + credit accounts prior to starting reconciliation, subject to both + a global and per-account preference. + + * src/gnome/dialog-transfer.c,h: Add interfaces to allow the + dialog-transfer to be manipulated as necessary to create the auto + interest transfer dialog. + + * src/scm/prefs.scm: added new Reconcile boolean: Automatic + Interest Transfer * src/engine/Account.c,h: Add access functions + for per-account auto interest xfer kvp + + * src/engine/kvp_doc.txt: Document new kvps. + 2001-06-17 James LewisMoss * src/engine/gnc-commodity-xml-v2.c (set_commodity_value): strip diff --git a/src/engine/Account.c b/src/engine/Account.c index e575d2dd67..bdf47494d9 100644 --- a/src/engine/Account.c +++ b/src/engine/Account.c @@ -2099,6 +2099,61 @@ xaccAccountClearReconcilePostpone (Account *account) /********************************************************************\ \********************************************************************/ +/* xaccAccountGetAutoInterestXfer: determine whether the auto interest + * xfer option is enabled for this account, and return that value. + * If it is not defined for the account, return the default value. + */ +gboolean +xaccAccountGetAutoInterestXfer (Account *account, gboolean default_value) +{ + kvp_value *value = NULL; + char *setting = NULL; + gboolean result = default_value; + + if ( ( account ) && + ( value = kvp_frame_get_slot_path (account->kvp_data, + "reconcile-info", + "auto-interest-transfer", + NULL) ) && + ( kvp_value_get_type (value) == KVP_TYPE_STRING ) && + ( setting = kvp_value_get_string(value) ) ) + { + if( !strcmp( setting, "true" ) ) + result = TRUE; + else if( !strcmp( setting, "false" ) ) + result = FALSE; + } + + return (result); +} + +/********************************************************************\ +\********************************************************************/ + +void +xaccAccountSetAutoInterestXfer (Account *account, gboolean option) +{ + kvp_frame *frame; + if (!account) + return; + + xaccAccountBeginEdit (account); + frame = kvp_frame_get_frame (account->kvp_data, + "reconcile-info", NULL); + + /* FIXME: need KVP_TYPE_BOOLEAN for this someday */ + + kvp_frame_set_slot_nc (frame, "auto-interest-transfer", + kvp_value_new_string (option ? "true" : "false")); + + mark_account (account); + account->core_dirty = TRUE; + xaccAccountCommitEdit (account); +} + +/********************************************************************\ +\********************************************************************/ + const char * xaccAccountGetLastNum (Account *account) { diff --git a/src/engine/Account.h b/src/engine/Account.h index 38be17ce62..f25ed88892 100644 --- a/src/engine/Account.h +++ b/src/engine/Account.h @@ -329,6 +329,9 @@ void xaccAccountSetReconcilePostponeBalance (Account *account, void xaccAccountClearReconcilePostpone (Account *account); +gboolean xaccAccountGetAutoInterestXfer (Account *account, gboolean default_value); +void xaccAccountSetAutoInterestXfer (Account *account, gboolean option); + /* Get and set the last num field of an Account */ const char * xaccAccountGetLastNum (Account *account); void xaccAccountSetLastNum (Account *account, const char *num); diff --git a/src/engine/kvp_doc.txt b/src/engine/kvp_doc.txt index f5f0a2effd..8d902ae321 100644 --- a/src/engine/kvp_doc.txt +++ b/src/engine/kvp_doc.txt @@ -93,6 +93,14 @@ Type: numeric Entities: Account Use: store the ending balance of a postponed reconciliation +Name: reconcile-info/auto-interest-transfer +Type: string +Entities: Account +Use: allows the user to override the global reconcile option + "Automatic interest transfer" on a per-account basis. + Acceptable values are "true" and "false". + (This really could use a KVP_TYPE_BOOLEAN.) + Name: split-type Entities: Split Use: xaccSplitGetType, xaccSplitMakeStockSplit diff --git a/src/gnome/dialog-transfer.c b/src/gnome/dialog-transfer.c index c7d60561fb..a6ad85ebfb 100644 --- a/src/gnome/dialog-transfer.c +++ b/src/gnome/dialog-transfer.c @@ -29,7 +29,6 @@ #include "EuroUtils.h" #include "FileDialog.h" #include "MultiLedger.h" -#include "account-tree.h" #include "dialog-transfer.h" #include "dialog-utils.h" #include "global-options.h" @@ -42,7 +41,6 @@ #include "messages.h" #include "query-user.h" #include "window-reconcile.h" -#include "QuickFill.h" #define DIALOG_TRANSFER_CM_CLASS "dialog-transfer" @@ -981,6 +979,63 @@ gnc_xfer_dialog_select_to_account(XferDialog *xferData, Account *account) } +static void +gnc_xfer_dialog_lock_account_tree(XferDialog *xferData, + XferDirection direction) +{ + GNCAccountTree *tree; + GtkWidget *show_button; + + if (xferData == NULL) + return; + + switch (direction) + { + case XFER_DIALOG_FROM: + tree = xferData->from; + show_button = xferData->from_show_button; + break; + case XFER_DIALOG_TO: + tree = xferData->to; + show_button = xferData->to_show_button; + break; + default: + return; + } + + gtk_widget_set_sensitive( GTK_WIDGET(tree), FALSE ); + gtk_widget_set_sensitive( GTK_WIDGET(show_button), FALSE ); +} + + +/********************************************************************\ + * gnc_xfer_dialog_lock_from_account_tree * + * prevent changes to the from account tree in an xfer dialog * + * * + * Args: xferData - xfer dialog structure * + * Return: none * +\********************************************************************/ +void +gnc_xfer_dialog_lock_from_account_tree(XferDialog *xferData) +{ + gnc_xfer_dialog_lock_account_tree(xferData, XFER_DIALOG_FROM); +} + + +/********************************************************************\ + * gnc_xfer_dialog_lock_to_account_tree * + * prevent changes to the to account tree in an xfer dialog * + * * + * Args: xferData - xfer dialog structure * + * Return: none * +\********************************************************************/ +void +gnc_xfer_dialog_lock_to_account_tree(XferDialog *xferData) +{ + gnc_xfer_dialog_lock_account_tree(xferData, XFER_DIALOG_TO); +} + + /********************************************************************\ * gnc_xfer_dialog_set_amount * * set the amount in the given xfer dialog * @@ -1026,6 +1081,22 @@ gnc_xfer_dialog_set_description(XferDialog *xferData, const char *description) gnc_quickfill_insert( xferData->qf, description, QUICKFILL_LIFO ); } +/********************************************************************\ + * gnc_xfer_dialog_set_date * + * set the date in the given xfer dialog * + * * + * Args: xferData - xfer dialog structure * + * set_date - the date to set * + * Return: none * +\********************************************************************/ +void +gnc_xfer_dialog_set_date(XferDialog *xferData, time_t set_date) +{ + if (xferData == NULL) + return; + + gnc_date_edit_set_time( GNC_DATE_EDIT(xferData->date_entry), set_date ); +} static void gnc_xfer_dialog_ok_cb(GtkWidget * widget, gpointer data) @@ -1545,3 +1616,153 @@ gnc_xfer_dialog (GtkWidget * parent, Account * initial) return xferData; } + +void +gnc_xfer_dialog_close( XferDialog *xferData ) +{ + if( xferData ) + gnc_close_gui_component_by_data (DIALOG_TRANSFER_CM_CLASS, xferData); +} + +void +gnc_xfer_dialog_set_title( XferDialog *xferData, const gchar *title ) +{ + if( xferData && title ) + { + gtk_window_set_title (GTK_WINDOW (xferData->dialog), title); + } +} + +void +gnc_xfer_dialog_set_information_frame_label( XferDialog *xferData, const gchar *label ) +{ + if( xferData && label ) + { + GtkWidget *frame = gnc_glade_lookup_widget (xferData->dialog, "transferinfo-frame" ); + gtk_frame_set_label( GTK_FRAME(frame), label ); + } +} + + +static void +gnc_xfer_dialog_set_account_frame_label( XferDialog *xferData, + const gchar *label, + XferDirection direction) +{ + if( xferData && label ) + { + /* "frame34" is the from frame, "frame35" is the to frame */ + GtkWidget *frame = gnc_glade_lookup_widget (xferData->dialog, + ( direction == XFER_DIALOG_FROM ? + "transferfrom-frame" : "transferto-frame" )); + gtk_frame_set_label( GTK_FRAME(frame), label ); + } +} + +void +gnc_xfer_dialog_set_from_account_frame_label( XferDialog *xferData, const gchar *label ) +{ + gnc_xfer_dialog_set_account_frame_label( xferData, label, XFER_DIALOG_FROM ); +} + +void +gnc_xfer_dialog_set_to_account_frame_label( XferDialog *xferData, const gchar *label ) +{ + gnc_xfer_dialog_set_account_frame_label( xferData, label, XFER_DIALOG_TO ); +} + +void +gnc_xfer_dialog_set_from_show_button_active( XferDialog *xferData, gboolean set_value ) +{ + if( xferData && xferData->from_show_button ) + { + gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(xferData->from_show_button), set_value ); + } +} + +void +gnc_xfer_dialog_set_to_show_button_active( XferDialog *xferData, gboolean set_value ) +{ + if( xferData && xferData->to_show_button ) + { + gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(xferData->to_show_button), set_value ); + } +} + +/* Add a button with a user-specified label and "clicked" callback */ +void gnc_xfer_dialog_add_user_specified_button( XferDialog *xferData, + const gchar *label, + GtkSignalFunc callback, + gpointer user_data ) +{ + if( xferData && label && callback ) + { + GtkWidget *button = gtk_button_new_with_label( label ); + GtkWidget *box = gnc_glade_lookup_widget (xferData->dialog, "transfermain-vbox" ); + gtk_box_pack_end( GTK_BOX(box), button, FALSE, FALSE, 0 ); + gtk_signal_connect( GTK_OBJECT(button), "clicked", + GTK_SIGNAL_FUNC( callback ), user_data ); + gtk_widget_show( button ); + } +} + +void gnc_xfer_dialog_toggle_currency_frame( XferDialog *xferData, + gboolean show_frame ) +{ + if( xferData && xferData->curr_transfer_frame ) + { + if( show_frame ) + gtk_widget_show( xferData->curr_transfer_frame ); + else + gtk_widget_hide( xferData->curr_transfer_frame ); + } +} + + +/* helper function */ +static gboolean +find_xfer (gpointer find_data, gpointer user_data) +{ + return( find_data == user_data ); +} + +/* Run the dialog until the user has either successfully completed the transaction + * (just clicking OK doesn't always count) or clicked Cancel. Return TRUE if the + * transaction was a success, FALSE otherwise. + */ +gboolean gnc_xfer_dialog_run_until_done( XferDialog *xferData ) +{ + if( xferData ) + { + while( TRUE ) + { + gint result = gnome_dialog_run( GNOME_DIALOG(xferData->dialog) ); + + if( result == 0 ) + { + /* See if the dialog is still there. For various reasons, the + * user could have hit OK but remained in the dialog. We don't + * want to return processing back to anyone else until we clear + * off this dialog, so if the dialog is still there we'll just + * run it again. + */ + + if( !gnc_find_first_gui_component( DIALOG_TRANSFER_CM_CLASS, + find_xfer, xferData ) ) + { + /* no more dialog, and OK was clicked, so assume it's all good */ + return( TRUE ); + } + /* else run the dialog again */ + + } + else /* result was Cancel */ + { + return( FALSE ); + } + } + } + + return( FALSE ); +} + diff --git a/src/gnome/dialog-transfer.h b/src/gnome/dialog-transfer.h index 418a4908f5..617bdf6273 100644 --- a/src/gnome/dialog-transfer.h +++ b/src/gnome/dialog-transfer.h @@ -25,18 +25,53 @@ #define __DIALOG_TRANSFER_H__ #include "Account.h" +#include "account-tree.h" +#include "QuickFill.h" typedef struct _xferDialog XferDialog; XferDialog * gnc_xfer_dialog(GtkWidget * parent, Account *initial); +gboolean gnc_xfer_dialog_run_until_done( XferDialog * ); + +void gnc_xfer_dialog_close( XferDialog * ); + +/*********** Access routines ***********/ +void gnc_xfer_dialog_set_title( XferDialog *, const gchar * ); + +/* set the label of the topmost frame */ +void gnc_xfer_dialog_set_information_frame_label( XferDialog *, const gchar * ); + +/* Add a button with a user-specified label and "clicked" callback. + * For now this doesn't offer a lot of flexibility, but it doesn't have to. + */ +void gnc_xfer_dialog_add_user_specified_button( XferDialog *xferData, + const gchar *label, + GtkSignalFunc callback, + gpointer user_data ); + +void gnc_xfer_dialog_toggle_currency_frame( XferDialog *xferData, + gboolean show_frame ); + +void gnc_xfer_dialog_set_from_account_frame_label( XferDialog *, const gchar * ); +void gnc_xfer_dialog_set_to_account_frame_label( XferDialog *, const gchar * ); + +/* set the buttons for "Show Income/Expense" */ +void gnc_xfer_dialog_set_from_show_button_active( XferDialog *, gboolean ); +void gnc_xfer_dialog_set_to_show_button_active( XferDialog *, gboolean ); + void gnc_xfer_dialog_select_from_account(XferDialog *xferData, Account *account); void gnc_xfer_dialog_select_to_account(XferDialog *xferData, Account *account); +/* prevent the user from changing an account tree */ +void gnc_xfer_dialog_lock_from_account_tree(XferDialog *xferData ); +void gnc_xfer_dialog_lock_to_account_tree(XferDialog *xferData ); + void gnc_xfer_dialog_set_amount(XferDialog *xferData, gnc_numeric amount); void gnc_xfer_dialog_set_description(XferDialog *xferData, const char *description); +void gnc_xfer_dialog_set_date(XferDialog *xferData, time_t set_time); #endif diff --git a/src/gnome/glade/transfer.glade b/src/gnome/glade/transfer.glade index 8decdf4fc5..8c6c588ada 100644 --- a/src/gnome/glade/transfer.glade +++ b/src/gnome/glade/transfer.glade @@ -79,7 +79,7 @@ GtkVBox - vbox78 + transfermain-vbox False 0 @@ -90,7 +90,7 @@ GtkFrame - frame33 + transferinfo-frame 3 0 @@ -311,7 +311,7 @@ GtkHBox - hbox66 + transfertrees-hbox False 0 @@ -322,7 +322,7 @@ GtkFrame - frame34 + transferfrom-frame 3 0 @@ -423,7 +423,7 @@ GtkFrame - frame35 + transferto-frame 3 0 diff --git a/src/gnome/window-reconcile.c b/src/gnome/window-reconcile.c index 49875154e7..4a011ef7a6 100644 --- a/src/gnome/window-reconcile.c +++ b/src/gnome/window-reconcile.c @@ -41,6 +41,7 @@ #include "date.h" #include "dialog-transfer.h" #include "dialog-utils.h" +#include "dialog-transfer.h" #include "global-options.h" #include "gnc-amount-edit.h" #include "gnc-component-manager.h" @@ -113,6 +114,41 @@ struct _RecnWindow gboolean delete_refresh; /* do a refresh upon a window deletion */ }; +/* This structure doesn't contain everything involved in the + * startRecnWindow, just pointers that have to be passed in to + * callbacks that need more than one piece of data to operate on. + * This is also used by the interest transfer dialog code. + */ +typedef struct _startRecnWindowData +{ + Account *account; /* the account being reconciled */ + GNCAccountType account_type; /* the type of the account */ + gboolean use_shares; /* whether to use shares for the account */ + + GtkWidget *startRecnWindow; /* the startRecnWindow dialog */ + GtkWidget *xfer_button; /* the dialog's interest transfer button */ + GNCAmountEdit *end_value; /* the dialog's ending balance amount edit */ + + XferDialog *xferData; /* the interest xfer dialog (if it exists) */ + + time_t date; /* the reconcile date for the interest xfer */ +} startRecnWindowData; + + +/* Note: make sure to update the help text for this in prefs.scm if these change! + * These macros define the account types for which an auto interest xfer dialog + * could pop up, if the user's preferences allow it. + */ +#define account_type_has_auto_interest_charge( type ) ( ( (type) == CREDIT ) || \ + ( (type) == LIABILITY ) ) + +#define account_type_has_auto_interest_payment( type ) ( ( (type) == BANK ) || \ + ( (type) == ASSET ) || \ + ( (type) == MUTUAL ) ) + +#define account_type_has_auto_interest_xfer( type ) \ + ( account_type_has_auto_interest_charge(type) || \ + account_type_has_auto_interest_payment(type) ) /** PROTOTYPES ******************************************************/ static gnc_numeric recnRecalculateBalance (RecnWindow *recnData); @@ -274,87 +310,220 @@ static gboolean gnc_start_recn_update_cb(GtkWidget *widget, GdkEventFocus *event, gpointer data) { - GNCAmountEdit *edit = data; - - gnc_amount_edit_evaluate (edit); + gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT(data)); return FALSE; } -/* gnc_start_recn_date_changed needs to know the - * account and the amount edit to update. Use this private - * structure to hand off that information. - * Don't need to include the date edit object since it's - * passed into gnc_start_recn_date_changed as the - * widget. - * - * Not sure if this is 100% the best way to do this... - */ - -typedef struct start_recn_callback_data -{ - Account *account; - GNCAmountEdit *gae; -} start_recn_callback_data; - -/* After the user has modified the end_value field, - * don't do any more auto updates for the remaining - * life of the startRecnWindow. Use this flag to keep track. - */ -static gboolean allow_auto_end_value_updates = TRUE; - -/* Note user changes to the startRecnWindow end_value amount edit */ -static void -gnc_start_recn_end_val_cb (GtkWidget *widget, gpointer data) -{ - allow_auto_end_value_updates = FALSE; -} - -/* If the user changed the date edit widget, and automatic - * ending balance updates are still allowed (i.e. the user - * hasn't manually updated the ending balance), update the +/* If the user changed the date edit widget, update the * ending balance to reflect the ending balance of the account * on the date that the date edit was changed to. */ static void -gnc_start_recn_date_changed (GtkWidget *widget, gpointer data) +gnc_start_recn_date_changed (GtkWidget *widget, startRecnWindowData *data) { GNCDateEdit *gde = GNC_DATE_EDIT (widget); - start_recn_callback_data *cb_data = data; - Account *acc = cb_data->account; - GNCAccountType type = xaccAccountGetType( acc ); - gboolean use_shares = ((type == STOCK) || (type == MUTUAL) || - (type == CURRENCY)); gnc_numeric new_balance; time_t new_date; - GNCAmountEdit *gae; - if (allow_auto_end_value_updates) - { new_date = gnc_date_edit_get_date_end (gde); /* get the balance for the account as of the new date */ - new_balance = use_shares ? - xaccAccountGetShareBalanceAsOfDate (acc, new_date) : - xaccAccountGetBalanceAsOfDate (acc, new_date); + new_balance = data->use_shares ? + xaccAccountGetShareBalanceAsOfDate (data->account, new_date) : + xaccAccountGetBalanceAsOfDate (data->account, new_date); - gae = cb_data->gae; + /* update the amount edit with the amount */ + gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT (data->end_value), new_balance); +} - /* Update the balance display widget, first blocking the "changed" - * signal to the callback above because this isn't a user change of - * the field. Otherwise the emitted "changed" signal would prevent - * future automatic updates of the end_value field. - */ - gtk_signal_handler_block_by_func( - GTK_OBJECT(gnc_amount_edit_gtk_entry (GNC_AMOUNT_EDIT (gae))), - GTK_SIGNAL_FUNC( gnc_start_recn_end_val_cb ), - gae ); - gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT (gae), new_balance); - gtk_signal_handler_unblock_by_func( - GTK_OBJECT(gnc_amount_edit_gtk_entry (GNC_AMOUNT_EDIT (gae))), - GTK_SIGNAL_FUNC( gnc_start_recn_end_val_cb ), - gae ); +/* For a given account, determine if an auto interest xfer dialog should be shown, + * based on both the per-account flag as well as the global reconcile option. + * The global option is the default that is used if there is no per-account option. + */ +static gboolean +gnc_recn_interest_xfer_get_auto_interest_xfer_allowed( Account *account ) +{ + return( xaccAccountGetAutoInterestXfer( account, + gnc_lookup_boolean_option( "Reconcile", + "Automatic interest transfer", + FALSE ) ) ); +} + +/********************************************************************\ + * recnInterestXferWindow * + * opens up a window to prompt the user to enter an interest * + * charge or payment for an account prior to reconciling it. * + * Only to be called for some types of accounts, as defined * + * in the macros at the top of this file. + * * + * NOTE: This function does not return until the user presses "Ok" * + * or "Cancel", which means that the transaction must be * + * resolved before the startRecnWindow will work. * + * * + * Args: data - jumbo structure containing info * + * about the start of the reconcile * + * process needed by this function. * + * Returns: none. * +\********************************************************************/ + +/* helper function */ +static char * +gnc_recn_make_interest_window_name(Account *account, char *text) +{ + char *fullname; + char *title; + + fullname = xaccAccountGetFullName(account, gnc_get_account_separator()); + title = g_strconcat(fullname, " - ", _(text), NULL); + + g_free(fullname); + + return title; +} + +/* user clicked button in the interest xfer dialog entitled + * "No Auto Interest Payments for this Account". + */ +static void +gnc_recn_interest_xfer_no_auto_clicked_cb(GtkButton *button, + startRecnWindowData *data) +{ + /* Indicate that the user doesn't want an auto interest xfer for this account. + */ + xaccAccountSetAutoInterestXfer( data->account, FALSE ); + + /* shut down the interest xfer dialog */ + gnc_xfer_dialog_close( data->xferData ); + + /* make the button clickable again */ + if( data->xfer_button ) + gtk_widget_set_sensitive(GTK_WIDGET(data->xfer_button), TRUE); +} + +static void +recnInterestXferWindow( startRecnWindowData *data) +{ + GtkWidget *frame; + GtkWidget *button; + gchar *title; + gint result; + + if( !account_type_has_auto_interest_xfer( data->account_type ) ) return; + + /* get a normal transfer dialog... */ + data->xferData = gnc_xfer_dialog( GTK_WIDGET(data->startRecnWindow), data->account ); + + /* ...and start changing things: */ + + /* change title */ + if( account_type_has_auto_interest_payment( data->account_type ) ) + title = gnc_recn_make_interest_window_name( data->account, "Interest Payment" ); + else + title = gnc_recn_make_interest_window_name( data->account, "Interest Charge" ); + + gnc_xfer_dialog_set_title( data->xferData, title ); + g_free( title ); + + + /* change frame labels */ + gnc_xfer_dialog_set_information_frame_label( data->xferData, _("Payment Information") ); + + /* interest accrued is a transaction from an income account to a bank account. + * interest charged is a transaction from a credit account to an expense account. + * The user isn't allowed to change the account (bank or credit) being reconciled. + */ + if( account_type_has_auto_interest_payment( data->account_type ) ) + { + gnc_xfer_dialog_set_from_account_frame_label( data->xferData, _("Payment From") ); + gnc_xfer_dialog_set_from_show_button_active( data->xferData, TRUE ); + + gnc_xfer_dialog_set_to_account_frame_label( data->xferData, _("Reconcile Account") ); + gnc_xfer_dialog_select_to_account( data->xferData, data->account ); + gnc_xfer_dialog_lock_to_account_tree( data->xferData ); } + else /* interest charged to account rather than paid to it */ + { + gnc_xfer_dialog_set_from_account_frame_label( data->xferData, _("Reconcile Account") ); + gnc_xfer_dialog_select_from_account( data->xferData, data->account ); + gnc_xfer_dialog_lock_from_account_tree( data->xferData ); + + gnc_xfer_dialog_set_to_account_frame_label( data->xferData, _("Payment To") ); + gnc_xfer_dialog_set_to_show_button_active( data->xferData, TRUE ); + } + + + /* add a button to disable auto interest payments for this account */ + gnc_xfer_dialog_add_user_specified_button( data->xferData, + ( account_type_has_auto_interest_payment( data->account_type ) ? + _("No Auto Interest Payments for this Account") + : _("No Auto Interest Charges for this Account") ), + GTK_SIGNAL_FUNC(gnc_recn_interest_xfer_no_auto_clicked_cb), + (gpointer) data ); + + /* no currency frame */ + gnc_xfer_dialog_toggle_currency_frame( data->xferData, FALSE ); + + /* set the reconcile date for the transaction date */ + gnc_xfer_dialog_set_date( data->xferData, data->date ); + + /* Now run the transfer dialog. This blocks until done. + * If the user hit Cancel, make the button clickable so that + * the user can retry if they want. We don't make the button + * clickable if they successfully entered a transaction, since + * the fact that the button was clickable again might make + * the user think that the transaction didn't actually go through. + */ + if( ! gnc_xfer_dialog_run_until_done( data->xferData ) ) + if( data->xfer_button ) + gtk_widget_set_sensitive(GTK_WIDGET(data->xfer_button), TRUE); + + /* done with the XferDialog */ + data->xferData = NULL; +} + +/* Set up for the interest xfer window, run the window, and update + * the startRecnWindow if the interest xfer changed anything that matters. + */ +static void +gnc_reconcile_interest_xfer_run(startRecnWindowData *data) +{ + GtkWidget *entry = gnc_amount_edit_gtk_entry( GNC_AMOUNT_EDIT(data->end_value) ); + gnc_numeric before = gnc_amount_edit_get_amount( GNC_AMOUNT_EDIT(data->end_value) ); + gnc_numeric after = gnc_numeric_zero(); + + recnInterestXferWindow( data ); + + /* recompute the ending balance */ + if (data->use_shares) + after = xaccAccountGetShareBalanceAsOfDate(data->account, data->date); + else + after = xaccAccountGetBalanceAsOfDate(data->account, data->date); + + /* update the ending balance in the startRecnWindow if it has changed. */ + if( gnc_numeric_compare( before, after ) ) + { + if (gnc_reverse_balance(data->account)) + after = gnc_numeric_neg (after); + + gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT (data->end_value), after); + gtk_widget_grab_focus(GTK_WIDGET(entry)); + gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1); + } +} + +static void +gnc_start_recn_interest_clicked_cb(GtkButton *button, startRecnWindowData *data) +{ + /* indicate in account that user wants an auto interest xfer for this account */ + xaccAccountSetAutoInterestXfer( data->account, TRUE ); + + /* make the button unclickable since we're popping up the window */ + if( data->xfer_button ) + gtk_widget_set_sensitive(GTK_WIDGET(data->xfer_button), FALSE); + + /* run the account window */ + gnc_reconcile_interest_xfer_run( data ); } /********************************************************************\ @@ -376,21 +545,30 @@ startRecnWindow(GtkWidget *parent, Account *account, gnc_numeric *new_ending, time_t *statement_date) { GtkWidget *dialog, *end_value, *date_value; - GNCAccountType account_type; + startRecnWindowData data = { NULL }; GNCPrintAmountInfo print_info; gnc_numeric ending; char *title; int result; + gboolean auto_interest_xfer_option; - /* This is a new startRecnWindow, so enable automatic - * updates of the ending balance amount edit widget. + /* Initialize the data structure that will be used for several callbacks + * throughout this file with the relevant info. Some initialization is + * done below as well. Note that local storage should be OK for this, + * since any callbacks using it will only work while the startRecnWindow + * is running. */ - allow_auto_end_value_updates = TRUE; + data.account = account; + data.account_type = xaccAccountGetType(account); + data.use_shares = ((data.account_type == STOCK) || + (data.account_type == MUTUAL) || + (data.account_type == CURRENCY)); + data.date = *statement_date; - account_type = xaccAccountGetType(account); + /* whether to have an automatic interest xfer dialog or not */ + auto_interest_xfer_option = gnc_recn_interest_xfer_get_auto_interest_xfer_allowed( account ); - if ((account_type == STOCK) || (account_type == MUTUAL) || - (account_type == CURRENCY)) + if( data.use_shares ) { ending = xaccAccountGetShareReconciledBalance(account); print_info = gnc_account_quantity_print_info (account, TRUE); @@ -416,6 +594,8 @@ startRecnWindow(GtkWidget *parent, Account *account, NULL); g_free (title); + data.startRecnWindow = GTK_WIDGET(dialog); + gnome_dialog_set_default(GNOME_DIALOG(dialog), 0); gnome_dialog_set_close(GNOME_DIALOG(dialog), TRUE); gnome_dialog_close_hides(GNOME_DIALOG(dialog), TRUE); @@ -433,26 +613,20 @@ startRecnWindow(GtkWidget *parent, Account *account, gtk_label_new(xaccPrintAmount (ending, print_info)); GtkWidget *vbox = GNOME_DIALOG(dialog)->vbox; GtkWidget *entry; - start_recn_callback_data cb_data = { NULL }; + GtkWidget *interest = NULL; date_value = gnc_date_edit_new(*statement_date, FALSE, FALSE); end_value = gnc_amount_edit_new (); - - /* Using local storage should be OK since this function doesn't return - * until the user is done with the dialog box. - */ - cb_data.account = account; - cb_data.gae = GNC_AMOUNT_EDIT (end_value); + data.end_value = GNC_AMOUNT_EDIT(end_value); /* need to get a callback on date changes to update the recn balance */ gtk_signal_connect ( GTK_OBJECT (date_value), "date_changed", - GTK_SIGNAL_FUNC (gnc_start_recn_date_changed), (gpointer) &cb_data ); + GTK_SIGNAL_FUNC (gnc_start_recn_date_changed), (gpointer) &data ); print_info.use_symbol = 0; gnc_amount_edit_set_print_info (GNC_AMOUNT_EDIT (end_value), print_info); - if ((account_type == STOCK) || (account_type == MUTUAL) || - (account_type == CURRENCY)) + if (data.use_shares) gnc_amount_edit_set_fraction (GNC_AMOUNT_EDIT (end_value), xaccAccountGetCommoditySCU (account)); else @@ -467,9 +641,6 @@ startRecnWindow(GtkWidget *parent, Account *account, gtk_signal_connect(GTK_OBJECT(entry), "focus-out-event", GTK_SIGNAL_FUNC(gnc_start_recn_update_cb), end_value); - gtk_signal_connect(GTK_OBJECT(entry), "changed", - GTK_SIGNAL_FUNC(gnc_start_recn_end_val_cb), end_value); - gnome_dialog_editable_enters(GNOME_DIALOG(dialog), GTK_EDITABLE(entry)); gtk_misc_set_alignment(GTK_MISC(date_title), 1.0, 0.5); @@ -494,12 +665,41 @@ startRecnWindow(GtkWidget *parent, Account *account, 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); + /* 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 + * dialog if it isn't automatically popping up. + */ + if( account_type_has_auto_interest_payment( data.account_type ) ) + interest = gtk_button_new_with_label( _("Enter Interest Payment...") ); + else if( account_type_has_auto_interest_charge( data.account_type ) ) + interest = gtk_button_new_with_label( _("Enter Interest Charge...") ); + + if( interest ) + { + data.xfer_button = interest; + + gtk_box_pack_end( GTK_BOX(vbox), interest, FALSE, FALSE, 0 ); + gtk_signal_connect(GTK_OBJECT(interest), "clicked", + GTK_SIGNAL_FUNC(gnc_start_recn_interest_clicked_cb), + (gpointer) &data ); + + if( auto_interest_xfer_option ) + gtk_widget_set_sensitive(GTK_WIDGET(interest), FALSE); + } + gtk_widget_show_all(dialog); gtk_widget_grab_focus(gnc_amount_edit_gtk_entry (GNC_AMOUNT_EDIT (end_value))); } + /* Allow the user to enter an interest payment or charge prior to reconciling */ + if( account_type_has_auto_interest_xfer( data.account_type ) + && auto_interest_xfer_option ) + { + gnc_reconcile_interest_xfer_run( &data ); + } + while (TRUE) { result = gnome_dialog_run(GNOME_DIALOG(dialog)); @@ -1535,13 +1735,6 @@ recnWindow (GtkWidget *parent, Account *account) recnData->account = *xaccAccountGetGUID (account); - recnData->component_id = - gnc_register_gui_component (WINDOW_RECONCILE_CM_CLASS, - refresh_handler, close_handler, - recnData); - - recn_set_watches (recnData); - type = xaccAccountGetType(account); recnData->use_shares = ((type == STOCK) || (type == MUTUAL) || @@ -1564,11 +1757,17 @@ recnWindow (GtkWidget *parent, Account *account) * ending balance for his/her bank statement */ if (!startRecnWindow (parent, account, &new_ending, &statement_date)) { - gnc_unregister_gui_component_by_data (WINDOW_RECONCILE_CM_CLASS, recnData); g_free (recnData); return NULL; } + recnData->component_id = + gnc_register_gui_component (WINDOW_RECONCILE_CM_CLASS, + refresh_handler, close_handler, + recnData); + + recn_set_watches (recnData); + last_statement_date = statement_date; recnData->new_ending = new_ending; diff --git a/src/scm/prefs.scm b/src/scm/prefs.scm index 7920f149b2..3014791d04 100644 --- a/src/scm/prefs.scm +++ b/src/scm/prefs.scm @@ -321,17 +321,25 @@ not each row") ;;; Reconcile Options +(gnc:register-configuration-option + (gnc:make-simple-boolean-option + (N_ "Reconcile") (N_ "Automatic interest transfer") + "a" (N_ "Prior to reconciling an account which charges or pays interest, \ +prompt the user to enter a transaction for the interest charge or payment. +Currently only enabled for Bank, Credit, Mutual, Asset, and Liability accounts.") + #f)) + (gnc:register-configuration-option (gnc:make-simple-boolean-option (N_ "Reconcile") (N_ "Automatic credit card payments") - "a" (N_ "After reconciling a credit card statement, prompt the user \ + "b" (N_ "After reconciling a credit card statement, prompt the user \ to enter a credit card payment") #t)) (gnc:register-configuration-option (gnc:make-simple-boolean-option (N_ "Reconcile") (N_ "Check off cleared transactions") - "b" (N_ "Automatically check off cleared transactions when reconciling") + "c" (N_ "Automatically check off cleared transactions when reconciling") #t))