mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
This affects many other components as well, including the python bindings git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@22942 57a11ea4-9604-0410-9ed3-97b8803252fd
946 lines
27 KiB
C
946 lines
27 KiB
C
/********************************************************************
|
|
* gnc-schedxactions-xml-v2.c -- xml routines for transactions *
|
|
* Copyright (C) 2001,2007 Joshua Sled <jsled@asynchronous.org> *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or *
|
|
* modify it under the terms of the GNU General Public License as *
|
|
* published by the Free Software Foundation; either version 2 of *
|
|
* the License, or (at your option) any later version. *
|
|
* *
|
|
* This program is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU General Public License*
|
|
* along with this program; if not, contact: *
|
|
* *
|
|
* Free Software Foundation Voice: +1-617-542-5942 *
|
|
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
|
|
* Boston, MA 02110-1301, USA gnu@gnu.org *
|
|
* *
|
|
*******************************************************************/
|
|
|
|
#include "config.h"
|
|
|
|
#include <glib.h>
|
|
#include <string.h>
|
|
|
|
#include "SX-book.h"
|
|
|
|
#include "gnc-xml-helper.h"
|
|
|
|
#include "sixtp.h"
|
|
#include "sixtp-utils.h"
|
|
#include "sixtp-parsers.h"
|
|
#include "sixtp-utils.h"
|
|
#include "sixtp-dom-parsers.h"
|
|
#include "sixtp-dom-generators.h"
|
|
|
|
#include "gnc-xml.h"
|
|
|
|
#include "io-gncxml-v2.h"
|
|
#include "io-gncxml-gen.h"
|
|
|
|
#include "sixtp-dom-parsers.h"
|
|
|
|
#undef G_LOG_DOMAIN
|
|
#define G_LOG_DOMAIN "gnc.backend.file.sx"
|
|
|
|
#define SX_ID "sx:id"
|
|
#define SX_NAME "sx:name"
|
|
#define SX_ENABLED "sx:enabled"
|
|
#define SX_AUTOCREATE "sx:autoCreate"
|
|
#define SX_AUTOCREATE_NOTIFY "sx:autoCreateNotify"
|
|
#define SX_ADVANCE_CREATE_DAYS "sx:advanceCreateDays"
|
|
#define SX_ADVANCE_REMIND_DAYS "sx:advanceRemindDays"
|
|
#define SX_INSTANCE_COUNT "sx:instanceCount"
|
|
#define SX_START "sx:start"
|
|
#define SX_LAST "sx:last"
|
|
#define SX_NUM_OCCUR "sx:num-occur"
|
|
#define SX_REM_OCCUR "sx:rem-occur"
|
|
#define SX_END "sx:end"
|
|
#define SX_TEMPL_ACCT "sx:templ-acct"
|
|
#define SX_FREQSPEC "sx:freqspec"
|
|
#define SX_SCHEDULE "sx:schedule"
|
|
#define SX_SLOTS "sx:slots"
|
|
#define SX_DEFER_INSTANCE "sx:deferredInstance"
|
|
|
|
/*
|
|
* FIXME: These should be defined in a header somewhere
|
|
*/
|
|
|
|
#define GNC_ACCOUNT_TAG "gnc:account"
|
|
#define GNC_TRANSACTION_TAG "gnc:transaction"
|
|
#define GNC_SCHEDXACTION_TAG "gnc:schedxaction"
|
|
|
|
const gchar *schedxaction_version_string = "1.0.0";
|
|
const gchar *schedxaction_version2_string = "2.0.0";
|
|
|
|
xmlNodePtr
|
|
gnc_schedXaction_dom_tree_create(SchedXaction *sx)
|
|
{
|
|
xmlNodePtr ret;
|
|
const GDate *date;
|
|
gint instCount;
|
|
const GncGUID *templ_acc_guid;
|
|
gboolean allow_2_2_incompat = TRUE;
|
|
|
|
templ_acc_guid = xaccAccountGetGUID(sx->template_acct);
|
|
|
|
/* FIXME: this should be the same as the def in io-gncxml-v2.c */
|
|
ret = xmlNewNode( NULL, BAD_CAST GNC_SCHEDXACTION_TAG );
|
|
|
|
if (allow_2_2_incompat)
|
|
xmlSetProp(ret, BAD_CAST "version", BAD_CAST schedxaction_version2_string);
|
|
else
|
|
xmlSetProp(ret, BAD_CAST "version", BAD_CAST schedxaction_version_string);
|
|
|
|
xmlAddChild( ret,
|
|
guid_to_dom_tree(SX_ID,
|
|
xaccSchedXactionGetGUID(sx)) );
|
|
|
|
xmlNewTextChild( ret, NULL, BAD_CAST SX_NAME, BAD_CAST xaccSchedXactionGetName(sx) );
|
|
|
|
if (allow_2_2_incompat)
|
|
{
|
|
xmlNewTextChild( ret, NULL, BAD_CAST SX_ENABLED,
|
|
BAD_CAST ( sx->enabled ? "y" : "n" ) );
|
|
}
|
|
|
|
xmlNewTextChild( ret, NULL, BAD_CAST SX_AUTOCREATE,
|
|
BAD_CAST ( sx->autoCreateOption ? "y" : "n" ) );
|
|
xmlNewTextChild( ret, NULL, BAD_CAST SX_AUTOCREATE_NOTIFY,
|
|
BAD_CAST ( sx->autoCreateNotify ? "y" : "n" ) );
|
|
xmlAddChild(ret, int_to_dom_tree(SX_ADVANCE_CREATE_DAYS,
|
|
sx->advanceCreateDays));
|
|
xmlAddChild(ret, int_to_dom_tree(SX_ADVANCE_REMIND_DAYS,
|
|
sx->advanceRemindDays));
|
|
|
|
instCount = gnc_sx_get_instance_count( sx, NULL );
|
|
xmlAddChild( ret, int_to_dom_tree( SX_INSTANCE_COUNT,
|
|
instCount ) );
|
|
|
|
xmlAddChild( ret,
|
|
gdate_to_dom_tree( SX_START,
|
|
xaccSchedXactionGetStartDate(sx) ) );
|
|
|
|
date = xaccSchedXactionGetLastOccurDate(sx);
|
|
if ( g_date_valid( date ) )
|
|
{
|
|
xmlAddChild( ret, gdate_to_dom_tree( SX_LAST, date ) );
|
|
}
|
|
|
|
if ( xaccSchedXactionHasOccurDef(sx) )
|
|
{
|
|
|
|
xmlAddChild(ret, int_to_dom_tree( SX_NUM_OCCUR,
|
|
xaccSchedXactionGetNumOccur(sx)));
|
|
xmlAddChild(ret, int_to_dom_tree( SX_REM_OCCUR,
|
|
xaccSchedXactionGetRemOccur(sx)));
|
|
|
|
}
|
|
else if ( xaccSchedXactionHasEndDate(sx) )
|
|
{
|
|
xmlAddChild( ret,
|
|
gdate_to_dom_tree( SX_END,
|
|
xaccSchedXactionGetEndDate(sx) ) );
|
|
}
|
|
|
|
/* output template account GncGUID */
|
|
xmlAddChild( ret,
|
|
guid_to_dom_tree(SX_TEMPL_ACCT,
|
|
templ_acc_guid));
|
|
|
|
if (allow_2_2_incompat)
|
|
{
|
|
xmlNodePtr schedule_node = xmlNewNode(NULL,
|
|
BAD_CAST "sx:schedule");
|
|
GList *schedule = gnc_sx_get_schedule(sx);
|
|
for (; schedule != NULL; schedule = schedule->next)
|
|
{
|
|
xmlAddChild(schedule_node, recurrence_to_dom_tree("gnc:recurrence", (Recurrence*)schedule->data));
|
|
}
|
|
xmlAddChild(ret, schedule_node);
|
|
}
|
|
|
|
/* Output deferred-instance list. */
|
|
{
|
|
xmlNodePtr instNode;
|
|
SXTmpStateData *tsd;
|
|
GList *l;
|
|
|
|
for ( l = gnc_sx_get_defer_instances( sx ); l; l = l->next )
|
|
{
|
|
tsd = (SXTmpStateData*)l->data;
|
|
|
|
instNode = xmlNewNode( NULL, BAD_CAST SX_DEFER_INSTANCE );
|
|
if ( g_date_valid( &tsd->last_date ) )
|
|
{
|
|
xmlAddChild( instNode, gdate_to_dom_tree( SX_LAST,
|
|
&tsd->last_date ) );
|
|
}
|
|
xmlAddChild( instNode, int_to_dom_tree( SX_REM_OCCUR,
|
|
tsd->num_occur_rem ) );
|
|
xmlAddChild( instNode, int_to_dom_tree( SX_INSTANCE_COUNT,
|
|
tsd->num_inst ) );
|
|
xmlAddChild( ret, instNode );
|
|
}
|
|
}
|
|
|
|
/* output kvp_frame */
|
|
{
|
|
xmlNodePtr kvpnode =
|
|
kvp_frame_to_dom_tree( SX_SLOTS,
|
|
xaccSchedXactionGetSlots(sx) );
|
|
if ( kvpnode )
|
|
{
|
|
xmlAddChild(ret, kvpnode);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
struct sx_pdata
|
|
{
|
|
SchedXaction *sx;
|
|
QofBook *book;
|
|
gboolean saw_freqspec;
|
|
gboolean saw_recurrence;
|
|
};
|
|
|
|
static
|
|
gboolean
|
|
sx_id_handler( xmlNodePtr node, gpointer sx_pdata )
|
|
{
|
|
struct sx_pdata *pdata = sx_pdata;
|
|
SchedXaction *sx = pdata->sx;
|
|
GncGUID *tmp = dom_tree_to_guid( node );
|
|
|
|
g_return_val_if_fail( tmp, FALSE );
|
|
xaccSchedXactionSetGUID(sx, tmp);
|
|
g_free( tmp );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static
|
|
gboolean
|
|
sx_name_handler( xmlNodePtr node, gpointer sx_pdata )
|
|
{
|
|
struct sx_pdata *pdata = sx_pdata;
|
|
SchedXaction *sx = pdata->sx;
|
|
gchar *tmp = dom_tree_to_text( node );
|
|
g_debug("sx named [%s]", tmp);
|
|
g_return_val_if_fail( tmp, FALSE );
|
|
xaccSchedXactionSetName( sx, tmp );
|
|
g_free( tmp );
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
sx_enabled_handler( xmlNodePtr node, gpointer sx_pdata )
|
|
{
|
|
struct sx_pdata *pdata = sx_pdata;
|
|
SchedXaction *sx = pdata->sx;
|
|
gchar *tmp = dom_tree_to_text( node );
|
|
|
|
sx->enabled = (g_strcmp0( tmp, "y" ) == 0 ? TRUE : FALSE );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
sx_autoCreate_handler( xmlNodePtr node, gpointer sx_pdata )
|
|
{
|
|
struct sx_pdata *pdata = sx_pdata;
|
|
SchedXaction *sx = pdata->sx;
|
|
gchar *tmp = dom_tree_to_text( node );
|
|
|
|
sx->autoCreateOption = (g_strcmp0( tmp, "y" ) == 0 ? TRUE : FALSE );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
sx_notify_handler( xmlNodePtr node, gpointer sx_pdata )
|
|
{
|
|
struct sx_pdata *pdata = sx_pdata;
|
|
SchedXaction *sx = pdata->sx;
|
|
gchar *tmp = dom_tree_to_text( node );
|
|
|
|
sx->autoCreateNotify = (g_strcmp0( tmp, "y" ) == 0 ? TRUE : FALSE );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
sx_advCreate_handler( xmlNodePtr node, gpointer sx_pdata )
|
|
{
|
|
struct sx_pdata *pdata = sx_pdata;
|
|
SchedXaction *sx = pdata->sx;
|
|
gint64 advCreate;
|
|
|
|
if ( ! dom_tree_to_integer( node, &advCreate ) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
xaccSchedXactionSetAdvanceCreation( sx, advCreate );
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
sx_advRemind_handler( xmlNodePtr node, gpointer sx_pdata )
|
|
{
|
|
struct sx_pdata *pdata = sx_pdata;
|
|
SchedXaction *sx = pdata->sx;
|
|
gint64 advRemind;
|
|
|
|
if ( ! dom_tree_to_integer( node, &advRemind ) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
xaccSchedXactionSetAdvanceReminder( sx, advRemind );
|
|
return TRUE;
|
|
}
|
|
|
|
static
|
|
gboolean
|
|
sx_set_date( xmlNodePtr node, SchedXaction *sx,
|
|
void (*settor)( SchedXaction *sx, const GDate *d ) )
|
|
{
|
|
GDate *date;
|
|
date = dom_tree_to_gdate( node );
|
|
g_return_val_if_fail( date, FALSE );
|
|
(*settor)( sx, date );
|
|
g_date_free( date );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static
|
|
gboolean
|
|
sx_instcount_handler( xmlNodePtr node, gpointer sx_pdata )
|
|
{
|
|
struct sx_pdata *pdata = sx_pdata;
|
|
SchedXaction *sx = pdata->sx;
|
|
gint64 instanceNum;
|
|
|
|
if ( ! dom_tree_to_integer( node, &instanceNum ) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
gnc_sx_set_instance_count( sx, instanceNum );
|
|
return TRUE;
|
|
}
|
|
|
|
static
|
|
gboolean
|
|
sx_start_handler( xmlNodePtr node, gpointer sx_pdata )
|
|
{
|
|
struct sx_pdata *pdata = sx_pdata;
|
|
SchedXaction *sx = pdata->sx;
|
|
|
|
return sx_set_date( node, sx, xaccSchedXactionSetStartDate );
|
|
}
|
|
|
|
static
|
|
gboolean
|
|
sx_last_handler( xmlNodePtr node, gpointer sx_pdata )
|
|
{
|
|
struct sx_pdata *pdata = sx_pdata;
|
|
SchedXaction *sx = pdata->sx;
|
|
|
|
return sx_set_date( node, sx, xaccSchedXactionSetLastOccurDate );
|
|
}
|
|
|
|
static
|
|
gboolean
|
|
sx_end_handler( xmlNodePtr node, gpointer sx_pdata )
|
|
{
|
|
struct sx_pdata *pdata = sx_pdata;
|
|
SchedXaction *sx = pdata->sx;
|
|
|
|
return sx_set_date( node, sx, xaccSchedXactionSetEndDate );
|
|
}
|
|
|
|
static void
|
|
_fixup_recurrence_start_dates(const GDate *sx_start_date, GList *schedule)
|
|
{
|
|
GList *iter;
|
|
for (iter = schedule; iter != NULL; iter = iter->next)
|
|
{
|
|
Recurrence *r;
|
|
GDate start, next;
|
|
|
|
r = (Recurrence*)iter->data;
|
|
|
|
start = *sx_start_date;
|
|
g_date_subtract_days(&start, 1);
|
|
|
|
g_date_clear(&next, 1);
|
|
|
|
recurrenceNextInstance(r, &start, &next);
|
|
g_return_if_fail(g_date_valid(&next));
|
|
|
|
{
|
|
gchar date_str[128];
|
|
gchar *sched_str;
|
|
|
|
g_date_strftime(date_str, 127, "%x", &next);
|
|
sched_str = recurrenceToString(r);
|
|
g_debug("setting recurrence [%s] start date to [%s]",
|
|
sched_str, date_str);
|
|
g_free(sched_str);
|
|
}
|
|
|
|
recurrenceSet(r,
|
|
recurrenceGetMultiplier(r),
|
|
recurrenceGetPeriodType(r),
|
|
&next,
|
|
recurrenceGetWeekendAdjust(r));
|
|
}
|
|
|
|
if (g_list_length(schedule) == 1
|
|
&& recurrenceGetPeriodType((Recurrence*)g_list_nth_data(schedule, 0)) == PERIOD_ONCE)
|
|
{
|
|
char date_buf[128];
|
|
Recurrence *fixup = (Recurrence*)g_list_nth_data(schedule, 0);
|
|
g_date_strftime(date_buf, 127, "%x", sx_start_date);
|
|
recurrenceSet(fixup, 1, PERIOD_ONCE, sx_start_date, WEEKEND_ADJ_NONE);
|
|
g_debug("fixed up period=ONCE Recurrence to date [%s]", date_buf);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
sx_freqspec_handler( xmlNodePtr node, gpointer sx_pdata )
|
|
{
|
|
struct sx_pdata *pdata = sx_pdata;
|
|
SchedXaction *sx = pdata->sx;
|
|
GList *schedule;
|
|
gchar* debug_str;
|
|
|
|
g_return_val_if_fail( node, FALSE );
|
|
|
|
schedule = dom_tree_freqSpec_to_recurrences(node, pdata->book);
|
|
gnc_sx_set_schedule(sx, schedule);
|
|
debug_str = recurrenceListToString(schedule);
|
|
g_debug("parsed from freqspec [%s]", debug_str);
|
|
g_free(debug_str);
|
|
|
|
_fixup_recurrence_start_dates(xaccSchedXactionGetStartDate(sx), schedule);
|
|
pdata->saw_freqspec = TRUE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
sx_schedule_recurrence_handler(xmlNodePtr node, gpointer parsing_data)
|
|
{
|
|
GList **schedule = (GList**)parsing_data;
|
|
gchar* sched_str;
|
|
Recurrence *r = dom_tree_to_recurrence(node);
|
|
g_return_val_if_fail(r, FALSE);
|
|
sched_str = recurrenceToString(r);
|
|
g_debug("parsed recurrence [%s]", sched_str);
|
|
g_free(sched_str);
|
|
*schedule = g_list_append(*schedule, r);
|
|
return TRUE;
|
|
}
|
|
|
|
struct dom_tree_handler sx_recurrence_list_handlers[] =
|
|
{
|
|
{ "gnc:recurrence", sx_schedule_recurrence_handler, 0, 0 },
|
|
{ NULL, NULL, 0, 0 }
|
|
};
|
|
|
|
static gboolean
|
|
sx_recurrence_handler(xmlNodePtr node, gpointer _pdata)
|
|
{
|
|
struct sx_pdata *parsing_data = _pdata;
|
|
GList *schedule = NULL;
|
|
gchar* debug_str;
|
|
|
|
g_return_val_if_fail(node, FALSE);
|
|
|
|
if (!dom_tree_generic_parse(node, sx_recurrence_list_handlers, &schedule))
|
|
return FALSE;
|
|
// g_return_val_if_fail(schedule, FALSE);
|
|
debug_str = recurrenceListToString(schedule);
|
|
g_debug("setting freshly-parsed schedule: [%s]", debug_str);
|
|
g_free(debug_str);
|
|
gnc_sx_set_schedule(parsing_data->sx, schedule);
|
|
parsing_data->saw_recurrence = TRUE;
|
|
return TRUE;
|
|
}
|
|
|
|
static
|
|
gboolean
|
|
sx_defer_last_handler( xmlNodePtr node, gpointer gpTSD )
|
|
{
|
|
GDate *gd;
|
|
SXTmpStateData *tsd = (SXTmpStateData*)gpTSD;
|
|
|
|
g_return_val_if_fail( node, FALSE );
|
|
gd = dom_tree_to_gdate( node );
|
|
g_return_val_if_fail( gd, FALSE );
|
|
tsd->last_date = *gd;
|
|
g_date_free( gd );
|
|
return TRUE;
|
|
}
|
|
|
|
static
|
|
gboolean
|
|
sx_defer_rem_occur_handler( xmlNodePtr node, gpointer gpTSD )
|
|
{
|
|
gint64 remOccur;
|
|
SXTmpStateData *tsd = (SXTmpStateData*)gpTSD;
|
|
g_return_val_if_fail( node, FALSE );
|
|
|
|
if ( ! dom_tree_to_integer( node, &remOccur ) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
tsd->num_occur_rem = remOccur;
|
|
return TRUE;
|
|
}
|
|
|
|
static
|
|
gboolean
|
|
sx_defer_inst_count_handler( xmlNodePtr node, gpointer gpTSD )
|
|
{
|
|
gint64 instCount;
|
|
SXTmpStateData *tsd = (SXTmpStateData*)gpTSD;
|
|
g_return_val_if_fail( node, FALSE );
|
|
|
|
if ( ! dom_tree_to_integer( node, &instCount ) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
tsd->num_inst = instCount;
|
|
return TRUE;
|
|
}
|
|
|
|
struct dom_tree_handler sx_defer_dom_handlers[] =
|
|
{
|
|
/* tag name, handler, opt, ? */
|
|
{ SX_LAST, sx_defer_last_handler, 1, 0 },
|
|
{ SX_REM_OCCUR, sx_defer_rem_occur_handler, 1, 0 },
|
|
{ SX_INSTANCE_COUNT, sx_defer_inst_count_handler, 1, 0 },
|
|
{ NULL, NULL, 0, 0 }
|
|
};
|
|
|
|
static
|
|
gboolean
|
|
sx_defer_inst_handler( xmlNodePtr node, gpointer sx_pdata )
|
|
{
|
|
struct sx_pdata *pdata = sx_pdata;
|
|
SchedXaction *sx = pdata->sx;
|
|
SXTmpStateData *tsd;
|
|
|
|
g_return_val_if_fail( node, FALSE );
|
|
|
|
tsd = g_new0( SXTmpStateData, 1 );
|
|
g_assert( sx_defer_dom_handlers != NULL );
|
|
if ( !dom_tree_generic_parse( node,
|
|
sx_defer_dom_handlers,
|
|
tsd ) )
|
|
{
|
|
xmlElemDump(stdout, NULL, node);
|
|
g_free( tsd );
|
|
tsd = NULL;
|
|
return FALSE;
|
|
}
|
|
|
|
/* We assume they were serialized in sorted order, here. */
|
|
sx->deferredList = g_list_append( sx->deferredList, tsd );
|
|
return TRUE;
|
|
}
|
|
|
|
static
|
|
gboolean
|
|
sx_numOccur_handler( xmlNodePtr node, gpointer sx_pdata )
|
|
{
|
|
struct sx_pdata *pdata = sx_pdata;
|
|
SchedXaction *sx = pdata->sx;
|
|
gint64 numOccur;
|
|
|
|
if ( ! dom_tree_to_integer( node, &numOccur ) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
xaccSchedXactionSetNumOccur( sx, numOccur );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static
|
|
gboolean
|
|
sx_templ_acct_handler( xmlNodePtr node, gpointer sx_pdata)
|
|
{
|
|
struct sx_pdata *pdata = sx_pdata;
|
|
SchedXaction *sx = pdata->sx;
|
|
GncGUID *templ_acct_guid = dom_tree_to_guid(node);
|
|
Account *account;
|
|
|
|
if (!templ_acct_guid)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
account = xaccAccountLookup(templ_acct_guid, pdata->book);
|
|
sx_set_template_account(sx, account);
|
|
g_free(templ_acct_guid);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static
|
|
gboolean
|
|
sx_remOccur_handler( xmlNodePtr node, gpointer sx_pdata )
|
|
{
|
|
struct sx_pdata *pdata = sx_pdata;
|
|
SchedXaction *sx = pdata->sx;
|
|
gint64 remOccur;
|
|
|
|
if ( ! dom_tree_to_integer( node, &remOccur ) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
xaccSchedXactionSetRemOccur( sx, remOccur );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static
|
|
gboolean
|
|
sx_slots_handler( xmlNodePtr node, gpointer sx_pdata )
|
|
{
|
|
struct sx_pdata *pdata = sx_pdata;
|
|
SchedXaction *sx = pdata->sx;
|
|
|
|
return dom_tree_to_kvp_frame_given( node, xaccSchedXactionGetSlots (sx) );
|
|
}
|
|
|
|
struct dom_tree_handler sx_dom_handlers[] =
|
|
{
|
|
{ SX_ID, sx_id_handler, 1, 0 },
|
|
{ SX_NAME, sx_name_handler, 1, 0 },
|
|
{ SX_ENABLED, sx_enabled_handler, 0, 0 },
|
|
{ SX_AUTOCREATE, sx_autoCreate_handler, 1, 0 },
|
|
{ SX_AUTOCREATE_NOTIFY, sx_notify_handler, 1, 0 },
|
|
{ SX_ADVANCE_CREATE_DAYS, sx_advCreate_handler, 1, 0 },
|
|
{ SX_ADVANCE_REMIND_DAYS, sx_advRemind_handler, 1, 0 },
|
|
{ SX_INSTANCE_COUNT, sx_instcount_handler, 0, 0 },
|
|
{ SX_START, sx_start_handler, 1, 0 },
|
|
{ SX_LAST, sx_last_handler, 0, 0 },
|
|
{ SX_NUM_OCCUR, sx_numOccur_handler, 0, 0 },
|
|
{ SX_REM_OCCUR, sx_remOccur_handler, 0, 0 },
|
|
{ SX_END, sx_end_handler, 0, 0 },
|
|
{ SX_TEMPL_ACCT, sx_templ_acct_handler, 0, 0 },
|
|
{ SX_FREQSPEC, sx_freqspec_handler, 0, 0 },
|
|
{ SX_SCHEDULE, sx_recurrence_handler, 0, 0 },
|
|
{ SX_DEFER_INSTANCE, sx_defer_inst_handler, 0, 0 },
|
|
{ SX_SLOTS, sx_slots_handler, 0, 0 },
|
|
{ NULL, NULL, 0, 0 }
|
|
};
|
|
|
|
static gboolean
|
|
gnc_schedXaction_end_handler(gpointer data_for_children,
|
|
GSList* data_from_children, GSList* sibling_data,
|
|
gpointer parent_data, gpointer global_data,
|
|
gpointer *result, const gchar *tag)
|
|
{
|
|
SchedXaction *sx;
|
|
gboolean successful = FALSE;
|
|
xmlNodePtr tree = (xmlNodePtr)data_for_children;
|
|
gxpf_data *gdata = (gxpf_data*)global_data;
|
|
struct sx_pdata sx_pdata;
|
|
|
|
if ( parent_data )
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
if ( !tag )
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
g_return_val_if_fail( tree, FALSE );
|
|
|
|
sx = xaccSchedXactionMalloc( gdata->bookdata );
|
|
|
|
memset(&sx_pdata, 0, sizeof(sx_pdata));
|
|
sx_pdata.sx = sx;
|
|
sx_pdata.book = gdata->bookdata;
|
|
|
|
g_assert( sx_dom_handlers != NULL );
|
|
|
|
successful = dom_tree_generic_parse( tree, sx_dom_handlers, &sx_pdata );
|
|
if (!successful)
|
|
{
|
|
g_critical("failed to parse scheduled xaction");
|
|
xmlElemDump( stdout, NULL, tree );
|
|
gnc_sx_begin_edit( sx );
|
|
xaccSchedXactionDestroy( sx );
|
|
goto done;
|
|
}
|
|
|
|
if (tree->properties)
|
|
{
|
|
gchar *sx_name = xaccSchedXactionGetName(sx);
|
|
xmlAttr *attr;
|
|
for (attr = tree->properties; attr != NULL; attr = attr->next)
|
|
{
|
|
xmlChar *attr_value = attr->children->content;
|
|
g_debug("sx attribute name[%s] value[%s]", attr->name, attr_value);
|
|
if (strcmp((const char *)attr->name, "version") != 0)
|
|
{
|
|
g_warning("unknown sx attribute [%s]", attr->name);
|
|
continue;
|
|
}
|
|
|
|
// if version == 1.0.0: ensure freqspec, no recurrence
|
|
// if version == 2.0.0: ensure recurrence, no freqspec.
|
|
if (strcmp((const char *)attr_value,
|
|
schedxaction_version_string) == 0)
|
|
{
|
|
if (!sx_pdata.saw_freqspec)
|
|
g_critical("did not see freqspec in version 1 sx [%s]", sx_name);
|
|
if (sx_pdata.saw_recurrence)
|
|
g_warning("saw recurrence in supposedly version 1 sx [%s]", sx_name);
|
|
}
|
|
|
|
if (strcmp((const char *)attr_value,
|
|
schedxaction_version2_string) == 0)
|
|
{
|
|
if (sx_pdata.saw_freqspec)
|
|
g_warning("saw freqspec in version 2 sx [%s]", sx_name);
|
|
if (!sx_pdata.saw_recurrence)
|
|
g_critical("did not find recurrence in version 2 sx [%s]", sx_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
// generic_callback -> book_callback: insert the SX in the book
|
|
gdata->cb( tag, gdata->parsedata, sx );
|
|
|
|
/* FIXME: this should be removed somewhere near 1.8 release time. */
|
|
if ( sx->template_acct == NULL )
|
|
{
|
|
Account *ra = NULL;
|
|
const char *id = NULL;
|
|
Account *acct = NULL;
|
|
sixtp_gdv2 *sixdata = gdata->parsedata;
|
|
QofBook *book;
|
|
|
|
book = sixdata->book;
|
|
|
|
/* We're dealing with a pre-200107<near-end-of-month> rgmerk
|
|
change re: storing template accounts. */
|
|
/* Fix: get account with name of our GncGUID from the template
|
|
accounts. Make that our template_acct pointer. */
|
|
/* THREAD-UNSAFE */
|
|
id = guid_to_string( xaccSchedXactionGetGUID( sx ) );
|
|
ra = gnc_book_get_template_root(book);
|
|
if ( ra == NULL )
|
|
{
|
|
g_warning( "Error getting template root account from being-parsed Book." );
|
|
xmlFreeNode( tree );
|
|
return FALSE;
|
|
}
|
|
acct = gnc_account_lookup_by_name( ra, id );
|
|
if ( acct == NULL )
|
|
{
|
|
g_warning("no template account with name [%s]", id);
|
|
xmlFreeNode( tree );
|
|
return FALSE;
|
|
}
|
|
g_debug("template account name [%s] for SX with GncGUID [%s]",
|
|
xaccAccountGetName( acct ), id );
|
|
|
|
/* FIXME: free existing template account.
|
|
* HUH????? We only execute this if there isn't
|
|
* currently an existing template account, don't we?
|
|
* <rgmerk>
|
|
*/
|
|
|
|
sx->template_acct = acct;
|
|
}
|
|
|
|
done:
|
|
xmlFreeNode( tree );
|
|
|
|
return successful;
|
|
}
|
|
|
|
sixtp*
|
|
gnc_schedXaction_sixtp_parser_create(void)
|
|
{
|
|
return sixtp_dom_parser_new( gnc_schedXaction_end_handler, NULL, NULL );
|
|
}
|
|
|
|
static
|
|
gboolean
|
|
tt_act_handler( xmlNodePtr node, gpointer data )
|
|
{
|
|
gnc_template_xaction_data *txd = data;
|
|
Account *acc;
|
|
gnc_commodity *com;
|
|
|
|
acc = dom_tree_to_account(node, txd->book);
|
|
|
|
if ( acc == NULL )
|
|
{
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
xaccAccountBeginEdit (acc);
|
|
|
|
/* Check for the lack of a commodity [signifying that the
|
|
pre-7/11/2001-CIT-change SX template Account was parsed [but
|
|
incorrectly]. */
|
|
if ( xaccAccountGetCommodity( acc ) == NULL )
|
|
{
|
|
#if 1
|
|
gnc_commodity_table* table;
|
|
|
|
table = gnc_commodity_table_get_table( txd->book );
|
|
com = gnc_commodity_table_lookup( table,
|
|
"template", "template" );
|
|
#else
|
|
/* FIXME: This should first look in the table of the
|
|
book, maybe? The right thing happens [WRT file
|
|
load/save] if we just _new all the time, but it
|
|
doesn't seem right. This whole block should go
|
|
away at some point, but the same concern still
|
|
applies for
|
|
SchedXaction.c:xaccSchedXactionInit... */
|
|
com = gnc_commodity_new( txd->book,
|
|
"template", "template",
|
|
"template", "template",
|
|
1 );
|
|
#endif
|
|
xaccAccountSetCommodity( acc, com );
|
|
}
|
|
|
|
txd->accts = g_list_append( txd->accts, acc );
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static
|
|
gboolean
|
|
tt_trn_handler( xmlNodePtr node, gpointer data )
|
|
{
|
|
gnc_template_xaction_data *txd = data;
|
|
Transaction *trn;
|
|
|
|
trn = dom_tree_to_transaction( node, txd->book );
|
|
|
|
if ( trn == NULL )
|
|
{
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
txd->transactions = g_list_append( txd->transactions, trn );
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
struct dom_tree_handler tt_dom_handlers[] =
|
|
{
|
|
{ GNC_ACCOUNT_TAG, tt_act_handler, 0, 0 },
|
|
{ GNC_TRANSACTION_TAG, tt_trn_handler, 0, 0 },
|
|
{ NULL, NULL, 0, 0 },
|
|
};
|
|
|
|
static gboolean
|
|
gnc_template_transaction_end_handler(gpointer data_for_children,
|
|
GSList* data_from_children,
|
|
GSList* sibling_data,
|
|
gpointer parent_data,
|
|
gpointer global_data,
|
|
gpointer *result,
|
|
const gchar *tag)
|
|
{
|
|
gboolean successful = FALSE;
|
|
xmlNodePtr tree = data_for_children;
|
|
gxpf_data *gdata = global_data;
|
|
QofBook *book = gdata->bookdata;
|
|
GList *n;
|
|
gnc_template_xaction_data txd;
|
|
|
|
txd.book = book;
|
|
txd.accts = NULL;
|
|
txd.transactions = NULL;
|
|
|
|
/* the DOM tree will have an account tree [the template
|
|
accounts] and a list of transactions [which will be members
|
|
of the template account].
|
|
|
|
we want to parse through the dom trees for each, placing
|
|
the null-parent account in the book's template-group slot,
|
|
the others under it, and the transactions as normal. */
|
|
|
|
if ( parent_data )
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
if ( !tag )
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
g_return_val_if_fail( tree, FALSE );
|
|
|
|
successful = dom_tree_generic_parse( tree, tt_dom_handlers, &txd );
|
|
|
|
if ( successful )
|
|
{
|
|
gdata->cb( tag, gdata->parsedata, &txd );
|
|
}
|
|
else
|
|
{
|
|
g_warning("failed to parse template transaction");
|
|
xmlElemDump( stdout, NULL, tree );
|
|
}
|
|
|
|
/* cleanup */
|
|
for ( n = txd.accts; n; n = n->next )
|
|
{
|
|
n->data = NULL;
|
|
}
|
|
for ( n = txd.transactions; n; n = n->next )
|
|
{
|
|
n->data = NULL;
|
|
}
|
|
g_list_free( txd.accts );
|
|
g_list_free( txd.transactions );
|
|
|
|
xmlFreeNode( tree );
|
|
|
|
return successful;
|
|
}
|
|
|
|
sixtp*
|
|
gnc_template_transaction_sixtp_parser_create( void )
|
|
{
|
|
return sixtp_dom_parser_new( gnc_template_transaction_end_handler,
|
|
NULL, NULL );
|
|
}
|