mirror of
https://github.com/Gnucash/gnucash.git
synced 2024-11-29 20:24:25 -06:00
1238b9d8cd
This will avoid a ninja-build from picking up a config.h generated by the autotools build (in the root build directory). Picking up the wrong config.h may lead to all kinds of subtle issues if the autotools run was done with different options than the cmake run.
942 lines
28 KiB
C++
942 lines
28 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 *
|
|
* *
|
|
*******************************************************************/
|
|
extern "C"
|
|
{
|
|
#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;
|
|
gchar* name = g_strdup (xaccSchedXactionGetName (sx));
|
|
|
|
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, checked_char_cast (name));
|
|
g_free (name);
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
/* xmlAddChild won't do anything with a NULL, so tests are superfluous. */
|
|
xmlAddChild (ret, qof_instance_slots_to_dom_tree (SX_SLOTS,
|
|
QOF_INSTANCE (sx)));
|
|
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 = static_cast<decltype (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 = static_cast<decltype (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 = static_cast<decltype (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 = static_cast<decltype (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 = static_cast<decltype (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 = static_cast<decltype (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 = static_cast<decltype (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 = static_cast<decltype (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 = static_cast<decltype (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 = static_cast<decltype (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 = static_cast<decltype (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 = static_cast<decltype (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 = static_cast<decltype (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 = static_cast<decltype (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 = static_cast<decltype (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 = static_cast<decltype (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 = static_cast<decltype (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 = static_cast<decltype (pdata)> (sx_pdata);
|
|
SchedXaction* sx = pdata->sx;
|
|
|
|
return dom_tree_create_instance_slots (node, QOF_INSTANCE (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 (static_cast<QofBook*> (gdata->bookdata));
|
|
|
|
memset (&sx_pdata, 0, sizeof (sx_pdata));
|
|
sx_pdata.sx = sx;
|
|
sx_pdata.book = static_cast<decltype (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;
|
|
Account* acct = NULL;
|
|
sixtp_gdv2* sixdata = static_cast<decltype (sixdata)> (gdata->parsedata);
|
|
QofBook* book;
|
|
gchar guidstr[GUID_ENCODING_LENGTH + 1];
|
|
|
|
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. */
|
|
guid_to_string_buff (xaccSchedXactionGetGUID (sx), guidstr);
|
|
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, guidstr);
|
|
if (acct == NULL)
|
|
{
|
|
g_warning ("no template account with name [%s]", guidstr);
|
|
xmlFreeNode (tree);
|
|
return FALSE;
|
|
}
|
|
g_debug ("template account name [%s] for SX with GncGUID [%s]",
|
|
xaccAccountGetName (acct), guidstr);
|
|
|
|
/* 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 = static_cast<decltype (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,
|
|
GNC_COMMODITY_NS_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", GNC_COMMODITY_NS_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 = static_cast<decltype (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 = static_cast<decltype (tree)> (data_for_children);
|
|
gxpf_data* gdata = static_cast<decltype (gdata)> (global_data);
|
|
QofBook* book = static_cast<decltype (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);
|
|
}
|