SX Since-Last-Run now reports transaction-creation errors to the user, degrades (somewhat) gracefully; report cleanup.

2006-01-29  Joshua Sled  <jsled@asynchronous.org>

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


git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@13035 57a11ea4-9604-0410-9ed3-97b8803252fd
zzzoldfeatures/register-rewrite
Joshua Sled 21 years ago
parent 6bd55f970d
commit eb8f68139b

@ -1,3 +1,16 @@
2006-01-29 Joshua Sled <jsled@asynchronous.org>
* 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 <hampton@employees.org>
* src/register/ledger-core/split-register.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<GString*> 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);
}

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

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

Loading…
Cancel
Save