diff --git a/ChangeLog b/ChangeLog index ab83c1bf00..ef8e10378c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +2006-01-29 Joshua Sled + + * src/report/utility-reports/welcome-to-gnucash.scm: register + menu item for self. + + * src/report/report-gnome/report-gnome.scm + (gnc:report-menu-setup): Remove report menu-item creation. + + * src/gnome/dialog-sxsincelast.c (create_each_transaction_helper): + Degrade more gracefully in the face of errors during scheduled-transaction + creation; record errors in-band rather than simply to the log, and + display them to the user. Resolves bugs: 151157, 167858, 151487. + 2006-01-29 David Hampton * src/register/ledger-core/split-register.c: diff --git a/src/gnome/dialog-sxsincelast.c b/src/gnome/dialog-sxsincelast.c index b90ad3360a..7de4e6202d 100644 --- a/src/gnome/dialog-sxsincelast.c +++ b/src/gnome/dialog-sxsincelast.c @@ -221,9 +221,12 @@ typedef struct toDeleteTuple_ { typedef struct creation_helper_userdata_ { /* the to-create tuple */ toCreateInstance *tci; - /* a [pointer to a] GList to append the GUIDs of newly-created + /* a pointer to a GList to append the GUIDs of newly-created * Transactions to, or NULL */ GList **createdGUIDs; + /* a pointer to a GList of error-messages encountered while + * creating the transactions. **/ + GList **creation_errors; } createData; /** @@ -316,10 +319,11 @@ static gboolean gnc_sxsld_commit_appr( sxSinceLastData *sxsld ); static void sxsincelast_entry_changed( GtkEditable *e, gpointer ud ); static void sxsincelast_destroy( GtkObject *o, gpointer ud ); static void sxsincelast_save_size( sxSinceLastData *sxsld ); -static void create_transactions_on( SchedXaction *sx, - GDate *gd, - toCreateInstance *tci, - GList **createdGUIDs ); +static void create_transactions_on(SchedXaction *sx, + GDate *gd, + toCreateInstance *tci, + GList **createdGUIDs, + GList **creation_errors); static gint create_each_transaction_helper( Transaction *t, void *d ); /* External for what reason ... ? */ void sxsl_get_sx_vars( SchedXaction *sx, GHashTable *varHash ); @@ -334,7 +338,7 @@ void print_vars_helper( gpointer key, static void clean_sincelast_data( sxSinceLastData *sxsld ); static void clean_variable_table( sxSinceLastData *sxsld ); -static void process_auto_create_list( GList *, sxSinceLastData *sxsld ); +static void process_auto_create_list(GList *, sxSinceLastData *sxsld, GList **creation_errors); static void add_to_create_list_to_gui( GList *, sxSinceLastData *sxsld ); static void add_reminders_to_gui( GList *, sxSinceLastData *sxsld ); static void add_dead_list_to_gui( GList *, sxSinceLastData *sxsld ); @@ -378,13 +382,17 @@ static void gnc_sxsld_free_sxState( gpointer key, gpointer userdata ); static void gnc_sxsld_free_entry_numeric( GObject *o, gpointer ud ); -static gint sxsld_process_to_create_instance( sxSinceLastData *sxsld, - toCreateInstance *tci ); +static gint sxsld_process_to_create_instance(sxSinceLastData *sxsld, + toCreateInstance *tci, + GList **creation_errors); static void sxsld_revert_to_create_txns( sxSinceLastData *sxsld, toCreateInstance *tci ); -static gint sxsld_create_to_create_txns( sxSinceLastData *sxsld, - toCreateInstance *tci ); +static gint sxsld_create_to_create_txns(sxSinceLastData *sxsld, + toCreateInstance *tci, + GList **creation_errors); static gint sxsld_get_future_created_txn_count( sxSinceLastData *sxsld ); +static void creation_errors_dialog(GList *creation_errors); +static void creation_errors_free(GList *creation_errors); static GtkActionEntry gnc_sxsld_menu_entries [] = { @@ -1068,8 +1076,9 @@ sxsld_revert_to_create_txns( sxSinceLastData *sxsld, **/ static gint -sxsld_create_to_create_txns( sxSinceLastData *sxsld, - toCreateInstance *tci ) +sxsld_create_to_create_txns(sxSinceLastData *sxsld, + toCreateInstance *tci, + GList **creation_errors) { gint toRet = 0; GList *l = NULL; @@ -1092,10 +1101,11 @@ sxsld_create_to_create_txns( sxSinceLastData *sxsld, sxsld_revert_to_create_txns( sxsld, tci ); } - create_transactions_on( tci->parentTCT->sx, - tci->date, - tci, - &created ); + create_transactions_on(tci->parentTCT->sx, + tci->date, + tci, + &created, + creation_errors); tci->dirty = FALSE; /* Add to the Query for that register. */ @@ -1122,8 +1132,9 @@ sxsld_create_to_create_txns( sxSinceLastData *sxsld, **/ static gint -sxsld_process_to_create_instance( sxSinceLastData *sxsld, - toCreateInstance *tci ) +sxsld_process_to_create_instance(sxSinceLastData *sxsld, + toCreateInstance *tci, + GList **creation_errors) { gint toRet = 0; @@ -1189,7 +1200,7 @@ sxsld_process_to_create_instance( sxSinceLastData *sxsld, break; case TO_CREATE: /* Go ahead and create... */ - toRet = sxsld_create_to_create_txns( sxsld, tci ); + toRet = sxsld_create_to_create_txns(sxsld, tci, creation_errors); break; default: g_assert( FALSE ); @@ -1244,7 +1255,7 @@ gboolean sxsld_process_to_create_page( sxSinceLastData *sxsld ) { GtkCTree *ct; - GList *tcList, *tcInstList; + GList *tcList, *tcInstList, *creation_errors; gboolean allVarsBound; toCreateTuple *tct; toCreateInstance *tci; @@ -1294,6 +1305,7 @@ sxsld_process_to_create_page( sxSinceLastData *sxsld ) tcList = sxsld->toCreateList; g_assert( tcList != NULL ); + creation_errors = NULL; gnc_suspend_gui_refresh(); for ( ; tcList ; tcList = tcList->next ) { tct = (toCreateTuple*)tcList->data; @@ -1303,10 +1315,15 @@ sxsld_process_to_create_page( sxSinceLastData *sxsld ) tcInstList = tcInstList->next ) { tci = (toCreateInstance*)tcInstList->data; - sxsld_process_to_create_instance( sxsld, tci ); + sxsld_process_to_create_instance(sxsld, tci, &creation_errors); } } gnc_resume_gui_refresh(); + if (g_list_length(creation_errors) > 0) + { + creation_errors_dialog(creation_errors); + creation_errors_free(creation_errors); + } return FALSE; } @@ -1526,6 +1543,7 @@ sxsincelast_init( sxSinceLastData *sxsld ) GtkWidget *w; GObject *o; GnomeDruidPage *nextPage; + GList *creation_errors; int i; static widgetSignalHandlerTuple widgets[] = { { SINCELAST_DRUID, "cancel", sxsincelast_druid_cancelled }, @@ -1643,7 +1661,13 @@ sxsincelast_init( sxSinceLastData *sxsld ) /* Do not call show_all here. Screws up the gtkuimanager code */ gtk_widget_show( sxsld->sincelast_window ); - process_auto_create_list( sxsld->autoCreateList, sxsld ); + creation_errors = NULL; + process_auto_create_list(sxsld->autoCreateList, sxsld, &creation_errors); + if (g_list_length(creation_errors) > 0) + { + creation_errors_dialog(creation_errors); + creation_errors_free(creation_errors); + } w = glade_xml_get_widget( sxsld->gxml, WHAT_TO_DO_PG ); nextPage = gnc_sxsld_get_appropriate_page( sxsld, @@ -1759,7 +1783,7 @@ _free_varBindings_hash_elts( gpointer key, gpointer value, gpointer data ) } static void -process_auto_create_list( GList *autoCreateList, sxSinceLastData *sxsld ) +process_auto_create_list(GList *autoCreateList, sxSinceLastData *sxsld, GList **creation_errors) { GList *l; toCreateTuple *tct; @@ -1784,7 +1808,7 @@ process_auto_create_list( GList *autoCreateList, sxSinceLastData *sxsld ) instances = instances->next ) { tci = (toCreateInstance*)instances->data; sxsld->autoCreatedCount += - sxsld_process_to_create_instance( sxsld, tci ); + sxsld_process_to_create_instance( sxsld, tci, creation_errors ); } } gnc_resume_gui_refresh(); @@ -1843,10 +1867,9 @@ add_to_create_list_to_gui( GList *toCreateList, sxSinceLastData *sxsld ) andequal_numerics_set, &allVarsBound ); rowText[1] = ( allVarsBound - ? _( "Ready to create" ) - /* READY_TEXT */ - : _( "Needs values for variables" ) - /* NEEDS_BINDINGS_TEXT */ ); + ? _( "Ready to create" ) /* READY_TEXT */ + : _( "Needs values for variables" ) /* NEEDS_BINDINGS_TEXT */ + ); break; case IGNORE: rowText[1] = _( "Ignored" ) /* IGNORE_TEXT */ ; @@ -2058,9 +2081,10 @@ processSelectedReminderList( GList *goodList, sxSinceLastData *sxsld ) /* special auto-create-opt processing; process it now. */ if ( autoCreateOpt ) { + GList *creation_errors = NULL; list = NULL; list = g_list_append( list, tct ); - process_auto_create_list( list, sxsld ); + process_auto_create_list( list, sxsld, &creation_errors ); list = NULL; } @@ -2216,7 +2240,13 @@ sxsincelast_populate( sxSinceLastData *sxsld ) /* if we're about to return a negative value [indicating only * auto-create no-notify txns], then actually create them. */ if ( toRet < 0 ) { - process_auto_create_list( sxsld->autoCreateList, sxsld ); + GList *creation_errors = NULL; + process_auto_create_list( sxsld->autoCreateList, sxsld, &creation_errors ); + if (g_list_length(creation_errors) > 0) + { + creation_errors_dialog(creation_errors); + creation_errors_free(creation_errors); + } } return toRet; @@ -2474,8 +2504,8 @@ create_each_transaction_helper( Transaction *t, void *d ) * keys, below. */ g_hash_table_insert( actualVars, g_strdup("i"), varIValue ); - for ( ; sList && osList ; - sList = sList->next, osList = osList->next ) { + for ( ; sList && osList; sList = sList->next, osList = osList->next) + { Account *acct; split = (Split*)sList->data; @@ -2495,29 +2525,48 @@ create_each_transaction_helper( Transaction *t, void *d ) GNC_SX_ID, GNC_SX_ACCOUNT, NULL ); - if ( kvp_val == NULL ) { - PERR( "Null kvp_val for account" ); + if (kvp_val == NULL) { + GString *err = g_string_new(""); + g_string_printf(err, "Null account kvp value for SX [%s], cancelling creation.", + xaccSchedXactionGetName(createUD->tci->parentTCT->sx)); + *createUD->creation_errors = g_list_append(*createUD->creation_errors, err); + errFlag = TRUE; + break; } acct_guid = kvp_value_get_guid( kvp_val ); - acct = xaccAccountLookup( acct_guid, - gnc_get_current_book ()); -#if 0 /* debug */ - DEBUG( "Got account with name \"%s\"", - xaccAccountGetName( acct ) ); -#endif /* 0 -- debug */ + acct = xaccAccountLookup( acct_guid, gnc_get_current_book ()); + if (acct == NULL) + { + const char *guidStr; + GString *err; + guidStr = guid_to_string((const GUID*)acct_guid); + err = g_string_new(""); + g_string_printf(err, "Unknown account for guid [%s], cancelling SX [%s] creation.", + guidStr, xaccSchedXactionGetName(createUD->tci->parentTCT->sx)); + g_free((char*)guidStr); + *createUD->creation_errors = g_list_append(*createUD->creation_errors, err); + errFlag = TRUE; + break; + } + if ( commonCommodity != NULL ) { if ( commonCommodity != xaccAccountGetCommodity( acct ) ) { - PERR( "Common-commodity difference: old=%s, new=%s\n", - gnc_commodity_get_mnemonic( commonCommodity ), - gnc_commodity_get_mnemonic( xaccAccountGetCommodity( acct ) ) ); + GString *err = g_string_new(""); + g_string_printf(err, "Common-commodity difference for SX [%s]: old=[%s], new=[%s]\n", + xaccSchedXactionGetName(createUD->tci->parentTCT->sx), + gnc_commodity_get_mnemonic(commonCommodity), + gnc_commodity_get_mnemonic(xaccAccountGetCommodity(acct))); + *createUD->creation_errors = g_list_append(*createUD->creation_errors, err); } } - commonCommodity = xaccAccountGetCommodity( acct ); - xaccAccountBeginEdit( acct ); - xaccAccountInsertSplit( acct, split ); + else + { + commonCommodity = xaccAccountGetCommodity( acct ); + } + xaccAccountBeginEdit(acct); + xaccAccountInsertSplit(acct, split); } - /* commonCommodity = xaccTransGetCurrency( t ); */ /* credit/debit formulas */ { char *str, *parseErrorLoc; @@ -2530,21 +2579,18 @@ create_each_transaction_helper( Transaction *t, void *d ) NULL); str = kvp_value_get_string( kvp_val ); credit_num = gnc_numeric_create( 0, 1 ); - if ( str != NULL - && strlen(str) != 0 ) { - if ( ! gnc_exp_parser_parse_separate_vars( str, &credit_num, - &parseErrorLoc, - actualVars ) ) { - PERR( "Error parsing credit formula \"%s\" at \"%s\": %s", - str, parseErrorLoc, gnc_exp_parser_error_string() ); - errFlag = TRUE; - break; + if (str != NULL && strlen(str) != 0) { + if (!gnc_exp_parser_parse_separate_vars(str, &credit_num, + &parseErrorLoc, + actualVars)) + { + GString *err = g_string_new(""); + g_string_printf(err, "Error parsing SX [%s] credit formula [%s] at [%s]: %s", + xaccSchedXactionGetName(createUD->tci->parentTCT->sx), + str, parseErrorLoc, gnc_exp_parser_error_string()); + *createUD->creation_errors = g_list_append(*createUD->creation_errors, err); + credit_num = gnc_numeric_create( 0, 1 ); } -#if 0 /* debug */ - DEBUG( "gnc_numeric::credit: \"%s\" -> %s [%s]", - str, gnc_numeric_to_string( credit_num ), - gnc_numeric_to_string( gnc_numeric_reduce( credit_num ) ) ); -#endif /* 0 -- debug */ } kvp_val = kvp_frame_get_slot_path( split_kvpf, @@ -2554,58 +2600,40 @@ create_each_transaction_helper( Transaction *t, void *d ) str = kvp_value_get_string( kvp_val ); debit_num = gnc_numeric_create( 0, 1 ); - if ( str != NULL - && strlen(str) != 0 ) { - if ( ! gnc_exp_parser_parse_separate_vars( str, &debit_num, - &parseErrorLoc, - actualVars ) ) { - PERR( "Error parsing debit_formula \"%s\" at \"%s\": %s", - str, parseErrorLoc, gnc_exp_parser_error_string() ); - errFlag = TRUE; - break; + if (str != NULL && strlen(str) != 0) { + if (!gnc_exp_parser_parse_separate_vars(str, &debit_num, + &parseErrorLoc, + actualVars)) + { + GString *err = g_string_new(""); + g_string_printf(err, "Error parsing SX [%s] debit formula [%s] at [%s]: %s", + xaccSchedXactionGetName(createUD->tci->parentTCT->sx), + str, parseErrorLoc, gnc_exp_parser_error_string()); + *createUD->creation_errors = g_list_append(*createUD->creation_errors, err); + debit_num = gnc_numeric_create( 0, 1 ); } -#if 0 /* debug */ - DEBUG( "gnc_numeric::debit: \"%s\" -> %s [%s]", - str, gnc_numeric_to_string( debit_num ), - gnc_numeric_to_string( gnc_numeric_reduce( debit_num ) ) ); -#endif /* 0 -- debug */ } final = gnc_numeric_sub_fixed( debit_num, credit_num ); - gncn_error = gnc_numeric_check( final ); - if ( gncn_error != GNC_ERROR_OK ) { - PERR( "Error %d in final gnc_numeric value", gncn_error ); - errFlag = TRUE; - break; + gncn_error = gnc_numeric_check(final); + if (gncn_error != GNC_ERROR_OK) { + GString *err = g_string_new(""); + g_string_printf(err, "Error %d in SX [%s] final gnc_numeric value, using 0 instead.", + gncn_error, + xaccSchedXactionGetName(createUD->tci->parentTCT->sx)); + *createUD->creation_errors = g_list_append(*createUD->creation_errors, err); + final = gnc_numeric_create(0, 1); } -#if 0 /* debug */ - DEBUG( "gnc_numeric::final: \"%s\"", - gnc_numeric_to_string( final ) ); -#endif /* 0 -- debug */ xaccSplitSetValue( split, final ); xaccSplitScrub( split ); } -#if 0 -/* NOT [YET] USED */ - kvp_val = kvp_frame_get_slot_path( split_kvpf, - GNC_SX_ID, - GNC_SX_SHARES, - NULL); - - kvp_val = kvp_frame_get_slot_path( split_kvpf, - GNC_SX_ID, - GNC_SX_AMNT, - NULL); -#endif /* 0 */ - xaccAccountCommitEdit( acct ); } - /* Cleanup actualVars table. */ { g_hash_table_foreach( actualVars, @@ -2615,31 +2643,33 @@ create_each_transaction_helper( Transaction *t, void *d ) actualVars = NULL; } - /* set the balancing currency. */ - if ( commonCommodity == NULL ) { - PERR( "Unable to find common currency/commodity." ); - } else { - xaccTransSetCurrency( newT, commonCommodity ); + if (commonCommodity == NULL) { + GString *err = g_string_new(""); + g_string_printf(err, "Unable to find common currency/commodity for SX [%s]; using default.", + xaccSchedXactionGetName(createUD->tci->parentTCT->sx)); + *createUD->creation_errors = g_list_append(*createUD->creation_errors, err); + commonCommodity = gnc_default_currency(); + } + xaccTransSetCurrency(newT, commonCommodity); + + if (errFlag) { + PERR("Some error in new transaction creation..."); + xaccTransRollbackEdit(newT); + xaccTransDestroy(newT); + xaccTransCommitEdit(newT); + return 13; } { kvp_frame *txn_frame; /* set a kvp-frame element in the transaction indicating and * pointing-to the SX this was created from. */ - txn_frame = xaccTransGetSlots( newT ); - kvp_frame_set_guid ( txn_frame, "from-sched-xaction", - xaccSchedXactionGetGUID(tci->parentTCT->sx) ); - } - - if ( errFlag ) { - PERR( "Some error in new transaction creation..." ); - xaccTransRollbackEdit( newT ); - xaccTransDestroy( newT ); - xaccTransCommitEdit( newT ); - return 13; + txn_frame = xaccTransGetSlots(newT); + kvp_frame_set_guid(txn_frame, "from-sched-xaction", + xaccSchedXactionGetGUID(tci->parentTCT->sx)); } - xaccTransCommitEdit( newT ); + xaccTransCommitEdit(newT); if ( createUD->createdGUIDs != NULL ) { *createUD->createdGUIDs = @@ -2655,34 +2685,39 @@ create_each_transaction_helper( Transaction *t, void *d ) * will set the last occur date incorrectly. **/ static void -create_transactions_on( SchedXaction *sx, - GDate *gd, - toCreateInstance *tci, - GList **createdGUIDs ) +create_transactions_on(SchedXaction *sx, + GDate *gd, + toCreateInstance *tci, + GList **createdGUIDs, + GList **creation_errors) { createData createUD; AccountGroup *ag; Account *acct; const char *id; - if ( tci ) { - g_assert( g_date_compare( gd, tci->date ) == 0 ); + if (tci) { + g_assert(g_date_compare(gd, tci->date) == 0); } ag = gnc_book_get_template_group( gnc_get_current_book () ); id = guid_to_string( xaccSchedXactionGetGUID(sx) ); - if ( ag && id ) { - /* This looks strange but it's right. The account is - named after the guid string. */ - acct = xaccGetAccountFromName( ag, id ); - if ( acct ) { - createUD.tci = tci; - createUD.createdGUIDs = createdGUIDs; - xaccAccountForEachTransaction( acct, - create_each_transaction_helper, - /*tct*/ &createUD ); - } + if ( !(ag && id) ) { + return; + } + /* This looks strange but it's right. The account is + named after the guid string. */ + acct = xaccGetAccountFromName( ag, id ); + if (!acct) { + return; } + + createUD.tci = tci; + createUD.createdGUIDs = createdGUIDs; + createUD.creation_errors = creation_errors; + xaccAccountForEachTransaction(acct, + create_each_transaction_helper, + /*tct*/ &createUD); } static void @@ -3050,15 +3085,9 @@ parse_vars_from_formula( const char *formula, toRet = 0; if ( ! gnc_exp_parser_parse_separate_vars( formula, num, &errLoc, varHash ) ) { -#if 0 /* just too verbose, as "Numeric errors" are acceptable in this - * context. */ - PERR( "Error parsing at \"%s\": %s", - errLoc, gnc_exp_parser_error_string() ); -#endif /* 0 */ toRet = -1; } - DEBUG( "result/num: %s", gnc_numeric_to_string( *num ) ); if ( !result ) { g_free( num ); } @@ -3896,3 +3925,34 @@ gnc_sxsld_commit_ledgers( sxSinceLastData *sxsld ) TRUE ); } +static +void +_adderror(gpointer data, gpointer user_data) +{ + GString *dialog_text = (GString*)user_data; + g_string_append_printf(dialog_text, "- %s\n", ((GString*)data)->str); +} + +static +void +creation_errors_dialog(GList *creation_errors) +{ + GString *dialog_text = g_string_new(_("The following errors were encountered while creating the Scheduled Transactions:\n")); + g_list_foreach(creation_errors, (GFunc)_adderror, dialog_text); + gnc_info_dialog(NULL, "%s", dialog_text->str); + g_string_free(dialog_text, TRUE); +} + +static void +_free_creation_errors(gpointer data, gpointer user_data_unused) +{ + g_string_free((GString*)data, TRUE); +} + +static +void +creation_errors_free(GList *creation_errors) +{ + g_list_foreach(creation_errors, (GFunc)_free_creation_errors, NULL); + g_list_free(creation_errors); +} diff --git a/src/report/report-gnome/report-gnome.scm b/src/report/report-gnome/report-gnome.scm index 1e932512db..2d5cfb183c 100644 --- a/src/report/report-gnome/report-gnome.scm +++ b/src/report/report-gnome/report-gnome.scm @@ -119,12 +119,4 @@ ;; push reports (new items added on top of menu) (gnc:add-report-template-menu-items) - ;; the Welcome to GnuCash "extravaganza" report - (gnc:add-extension - (gnc:make-menu-item - (N_ "Welcome Sample Report") - (N_ "Welcome-to-GnuCash report screen") - (list gnc:menuname-reports gnc:menuname-utility "") - (lambda (window) - (gnc:main-window-open-report (gnc:make-welcome-report) window)))) ) diff --git a/src/report/utility-reports/welcome-to-gnucash.scm b/src/report/utility-reports/welcome-to-gnucash.scm index 4cb0bf2d4a..bc3bf8d3f1 100644 --- a/src/report/utility-reports/welcome-to-gnucash.scm +++ b/src/report/utility-reports/welcome-to-gnucash.scm @@ -86,7 +86,7 @@ (gnc:define-report 'name (N_ "Welcome to GnuCash") - 'in-menu? #f + 'version 1 + 'menu-path (list gnc:menuname-utility) 'options-generator options 'renderer renderer) -