gnucash/libgnucash/backend/xml/gnc-transaction-xml-v2.cpp
2023-02-16 09:20:59 +00:00

638 lines
17 KiB
C++

/********************************************************************
* gnc-transactions-xml-v2.c -- xml routines for transactions *
* Copyright (C) 2001 Rob Browning *
* Copyright (C) 2002 Linas Vepstas <linas@linas.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 <glib.h>
#include <config.h>
#include <string.h>
#include "AccountP.h"
#include "Transaction.h"
#include "TransactionP.h"
#include "gnc-lot.h"
#include "gnc-lot-p.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-gen.h"
#include "sixtp-dom-parsers.h"
[[maybe_unused]] static const QofLogModule log_module = G_LOG_DOMAIN;
const gchar* transaction_version_string = "2.0.0";
static void
add_gnc_num (xmlNodePtr node, const gchar* tag, gnc_numeric num)
{
xmlAddChild (node, gnc_numeric_to_dom_tree (tag, &num));
}
static void
add_time64 (xmlNodePtr node, const gchar * tag, time64 time, gboolean always)
{
if (always || time)
xmlAddChild (node, time64_to_dom_tree (tag, time));
}
static xmlNodePtr
split_to_dom_tree (const gchar* tag, Split* spl)
{
xmlNodePtr ret;
ret = xmlNewNode (NULL, BAD_CAST tag);
xmlAddChild (ret, guid_to_dom_tree ("split:id", xaccSplitGetGUID (spl)));
{
char* memo = g_strdup (xaccSplitGetMemo (spl));
if (memo && g_strcmp0 (memo, "") != 0)
{
xmlNewTextChild (ret, NULL, BAD_CAST "split:memo",
checked_char_cast (memo));
}
g_free (memo);
}
{
char* action = g_strdup (xaccSplitGetAction (spl));
if (action && g_strcmp0 (action, "") != 0)
{
xmlNewTextChild (ret, NULL, BAD_CAST "split:action",
checked_char_cast (action));
}
g_free (action);
}
{
char tmp[2];
tmp[0] = xaccSplitGetReconcile (spl);
tmp[1] = '\0';
xmlNewTextChild (ret, NULL, BAD_CAST "split:reconciled-state",
BAD_CAST tmp);
}
add_time64 (ret, "split:reconcile-date",
xaccSplitGetDateReconciled (spl), FALSE);
add_gnc_num (ret, "split:value", xaccSplitGetValue (spl));
add_gnc_num (ret, "split:quantity", xaccSplitGetAmount (spl));
{
Account* account = xaccSplitGetAccount (spl);
xmlAddChild (ret, guid_to_dom_tree ("split:account",
xaccAccountGetGUID (account)));
}
{
GNCLot* lot = xaccSplitGetLot (spl);
if (lot)
{
xmlAddChild (ret, guid_to_dom_tree ("split:lot",
gnc_lot_get_guid (lot)));
}
}
/* xmlAddChild won't do anything with a NULL, so tests are superfluous. */
xmlAddChild (ret, qof_instance_slots_to_dom_tree ("split:slots",
QOF_INSTANCE (spl)));
return ret;
}
static void
add_trans_splits (xmlNodePtr node, Transaction* trn)
{
GList* n;
xmlNodePtr toaddto;
toaddto = xmlNewChild (node, NULL, BAD_CAST "trn:splits", NULL);
for (n = xaccTransGetSplitList (trn); n; n = n->next)
{
Split* s = static_cast<decltype (s)> (n->data);
xmlAddChild (toaddto, split_to_dom_tree ("trn:split", s));
}
}
xmlNodePtr
gnc_transaction_dom_tree_create (Transaction* trn)
{
xmlNodePtr ret;
gchar* str = NULL;
ret = xmlNewNode (NULL, BAD_CAST "gnc:transaction");
xmlSetProp (ret, BAD_CAST "version",
BAD_CAST transaction_version_string);
xmlAddChild (ret, guid_to_dom_tree ("trn:id", xaccTransGetGUID (trn)));
xmlAddChild (ret, commodity_ref_to_dom_tree ("trn:currency",
xaccTransGetCurrency (trn)));
str = g_strdup (xaccTransGetNum (trn));
if (str && (g_strcmp0 (str, "") != 0))
{
xmlNewTextChild (ret, NULL, BAD_CAST "trn:num",
checked_char_cast (str));
}
g_free (str);
add_time64 (ret, "trn:date-posted", xaccTransRetDatePosted (trn), TRUE);
add_time64 (ret, "trn:date-entered",
xaccTransRetDateEntered (trn), TRUE);
str = g_strdup (xaccTransGetDescription (trn));
if (str)
{
xmlNewTextChild (ret, NULL, BAD_CAST "trn:description",
checked_char_cast (str));
}
g_free (str);
/* xmlAddChild won't do anything with a NULL, so tests are superfluous. */
xmlAddChild (ret, qof_instance_slots_to_dom_tree ("trn:slots",
QOF_INSTANCE (trn)));
add_trans_splits (ret, trn);
return ret;
}
/***********************************************************************/
struct split_pdata
{
Split* split;
QofBook* book;
};
static inline gboolean
set_spl_string (xmlNodePtr node, Split* spl,
void (*func) (Split* spl, const char* txt))
{
gchar* tmp = dom_tree_to_text (node);
g_return_val_if_fail (tmp, FALSE);
func (spl, tmp);
g_free (tmp);
return TRUE;
}
static inline gboolean
set_spl_gnc_num (xmlNodePtr node, Split* spl,
void (*func) (Split* spl, gnc_numeric gn))
{
gnc_numeric* num = dom_tree_to_gnc_numeric (node);
g_return_val_if_fail (num, FALSE);
func (spl, *num);
g_free (num);
return FALSE;
}
static gboolean
spl_id_handler (xmlNodePtr node, gpointer data)
{
struct split_pdata* pdata = static_cast<decltype (pdata)> (data);
GncGUID* tmp = dom_tree_to_guid (node);
g_return_val_if_fail (tmp, FALSE);
xaccSplitSetGUID (pdata->split, tmp);
guid_free (tmp);
return TRUE;
}
static gboolean
spl_memo_handler (xmlNodePtr node, gpointer data)
{
struct split_pdata* pdata = static_cast<decltype (pdata)> (data);
return set_spl_string (node, pdata->split, xaccSplitSetMemo);
}
static gboolean
spl_action_handler (xmlNodePtr node, gpointer data)
{
struct split_pdata* pdata = static_cast<decltype (pdata)> (data);
return set_spl_string (node, pdata->split, xaccSplitSetAction);
}
static gboolean
spl_reconciled_state_handler (xmlNodePtr node, gpointer data)
{
struct split_pdata* pdata = static_cast<decltype (pdata)> (data);
gchar* tmp = dom_tree_to_text (node);
g_return_val_if_fail (tmp, FALSE);
xaccSplitSetReconcile (pdata->split, tmp[0]);
g_free (tmp);
return TRUE;
}
static gboolean
spl_reconcile_date_handler (xmlNodePtr node, gpointer data)
{
struct split_pdata* pdata = static_cast<decltype (pdata)> (data);
time64 time = dom_tree_to_time64 (node);
if (!dom_tree_valid_time64 (time, node->name)) time = 0;
xaccSplitSetDateReconciledSecs (pdata->split, time);
return TRUE;
}
static gboolean
spl_value_handler (xmlNodePtr node, gpointer data)
{
struct split_pdata* pdata = static_cast<decltype (pdata)> (data);
return set_spl_gnc_num (node, pdata->split, xaccSplitSetValue);
}
static gboolean
spl_quantity_handler (xmlNodePtr node, gpointer data)
{
struct split_pdata* pdata = static_cast<decltype (pdata)> (data);
return set_spl_gnc_num (node, pdata->split, xaccSplitSetAmount);
}
gboolean gnc_transaction_xml_v2_testing = FALSE;
static gboolean
spl_account_handler (xmlNodePtr node, gpointer data)
{
struct split_pdata* pdata = static_cast<decltype (pdata)> (data);
GncGUID* id = dom_tree_to_guid (node);
Account* account;
g_return_val_if_fail (id, FALSE);
account = xaccAccountLookup (id, pdata->book);
if (!account && gnc_transaction_xml_v2_testing &&
!guid_equal (id, guid_null ()))
{
account = xaccMallocAccount (pdata->book);
xaccAccountSetGUID (account, id);
xaccAccountSetCommoditySCU (account,
xaccSplitGetAmount (pdata->split).denom);
}
xaccAccountInsertSplit (account, pdata->split);
guid_free (id);
return TRUE;
}
static gboolean
spl_lot_handler (xmlNodePtr node, gpointer data)
{
struct split_pdata* pdata = static_cast<decltype (pdata)> (data);
GncGUID* id = dom_tree_to_guid (node);
GNCLot* lot;
g_return_val_if_fail (id, FALSE);
lot = gnc_lot_lookup (id, pdata->book);
if (!lot && gnc_transaction_xml_v2_testing &&
!guid_equal (id, guid_null ()))
{
lot = gnc_lot_new (pdata->book);
gnc_lot_set_guid (lot, *id);
}
gnc_lot_add_split (lot, pdata->split);
guid_free (id);
return TRUE;
}
static gboolean
spl_slots_handler (xmlNodePtr node, gpointer data)
{
struct split_pdata* pdata = static_cast<decltype (pdata)> (data);
gboolean successful;
successful = dom_tree_create_instance_slots (node,
QOF_INSTANCE (pdata->split));
g_return_val_if_fail (successful, FALSE);
return TRUE;
}
struct dom_tree_handler spl_dom_handlers[] =
{
{ "split:id", spl_id_handler, 1, 0 },
{ "split:memo", spl_memo_handler, 0, 0 },
{ "split:action", spl_action_handler, 0, 0 },
{ "split:reconciled-state", spl_reconciled_state_handler, 1, 0 },
{ "split:reconcile-date", spl_reconcile_date_handler, 0, 0 },
{ "split:value", spl_value_handler, 1, 0 },
{ "split:quantity", spl_quantity_handler, 1, 0 },
{ "split:account", spl_account_handler, 1, 0 },
{ "split:lot", spl_lot_handler, 0, 0 },
{ "split:slots", spl_slots_handler, 0, 0 },
{ NULL, NULL, 0, 0 },
};
static Split*
dom_tree_to_split (xmlNodePtr node, QofBook* book)
{
struct split_pdata pdata;
Split* ret;
g_return_val_if_fail (book, NULL);
ret = xaccMallocSplit (book);
g_return_val_if_fail (ret, NULL);
pdata.split = ret;
pdata.book = book;
/* this isn't going to work in a testing setup */
if (dom_tree_generic_parse (node, spl_dom_handlers, &pdata))
{
return ret;
}
else
{
xaccSplitDestroy (ret);
return NULL;
}
}
/***********************************************************************/
struct trans_pdata
{
Transaction* trans;
QofBook* book;
};
static inline gboolean
set_tran_string (xmlNodePtr node, Transaction* trn,
void (*func) (Transaction* trn, const char* txt))
{
gchar* tmp;
tmp = dom_tree_to_text (node);
g_return_val_if_fail (tmp, FALSE);
func (trn, tmp);
g_free (tmp);
return TRUE;
}
static gboolean
set_tran_time64 (xmlNodePtr node, Transaction * trn,
void (*func) (Transaction *, time64))
{
time64 time = dom_tree_to_time64 (node);
if (!dom_tree_valid_time64 (time, node->name)) time = 0;
func (trn, time);
return TRUE;
}
static gboolean
trn_id_handler (xmlNodePtr node, gpointer trans_pdata)
{
struct trans_pdata* pdata = static_cast<decltype (pdata)> (trans_pdata);
Transaction* trn = pdata->trans;
GncGUID* tmp = dom_tree_to_guid (node);
g_return_val_if_fail (tmp, FALSE);
xaccTransSetGUID ((Transaction*)trn, tmp);
guid_free (tmp);
return TRUE;
}
static gboolean
trn_currency_handler (xmlNodePtr node, gpointer trans_pdata)
{
struct trans_pdata* pdata = static_cast<decltype (pdata)> (trans_pdata);
Transaction* trn = pdata->trans;
gnc_commodity* ref;
ref = dom_tree_to_commodity_ref (node, pdata->book);
xaccTransSetCurrency (trn, ref);
return TRUE;
}
static gboolean
trn_num_handler (xmlNodePtr node, gpointer trans_pdata)
{
struct trans_pdata* pdata = static_cast<decltype (pdata)> (trans_pdata);
Transaction* trn = pdata->trans;
return set_tran_string (node, trn, xaccTransSetNum);
}
static gboolean
trn_date_posted_handler (xmlNodePtr node, gpointer trans_pdata)
{
struct trans_pdata* pdata = static_cast<decltype (pdata)> (trans_pdata);
Transaction* trn = pdata->trans;
return set_tran_time64 (node, trn, xaccTransSetDatePostedSecs);
}
static gboolean
trn_date_entered_handler (xmlNodePtr node, gpointer trans_pdata)
{
struct trans_pdata* pdata = static_cast<decltype (pdata)> (trans_pdata);
Transaction* trn = pdata->trans;
return set_tran_time64 (node, trn, xaccTransSetDateEnteredSecs);
}
static gboolean
trn_description_handler (xmlNodePtr node, gpointer trans_pdata)
{
struct trans_pdata* pdata = static_cast<decltype (pdata)> (trans_pdata);
Transaction* trn = pdata->trans;
return set_tran_string (node, trn, xaccTransSetDescription);
}
static gboolean
trn_slots_handler (xmlNodePtr node, gpointer trans_pdata)
{
struct trans_pdata* pdata = static_cast<decltype (pdata)> (trans_pdata);
Transaction* trn = pdata->trans;
gboolean successful;
successful = dom_tree_create_instance_slots (node, QOF_INSTANCE (trn));
g_return_val_if_fail (successful, FALSE);
return TRUE;
}
static gboolean
trn_splits_handler (xmlNodePtr node, gpointer trans_pdata)
{
struct trans_pdata* pdata = static_cast<decltype (pdata)> (trans_pdata);
Transaction* trn = pdata->trans;
xmlNodePtr mark;
g_return_val_if_fail (node, FALSE);
g_return_val_if_fail (node->xmlChildrenNode, FALSE);
for (mark = node->xmlChildrenNode; mark; mark = mark->next)
{
Split* spl;
if (g_strcmp0 ("text", (char*)mark->name) == 0)
continue;
if (g_strcmp0 ("trn:split", (char*)mark->name))
{
return FALSE;
}
spl = dom_tree_to_split (mark, pdata->book);
if (spl)
{
xaccTransAppendSplit (trn, spl);
}
else
{
return FALSE;
}
}
return TRUE;
}
struct dom_tree_handler trn_dom_handlers[] =
{
{ "trn:id", trn_id_handler, 1, 0 },
{ "trn:currency", trn_currency_handler, 0, 0},
{ "trn:num", trn_num_handler, 0, 0 },
{ "trn:date-posted", trn_date_posted_handler, 1, 0 },
{ "trn:date-entered", trn_date_entered_handler, 1, 0 },
{ "trn:description", trn_description_handler, 0, 0 },
{ "trn:slots", trn_slots_handler, 0, 0 },
{ "trn:splits", trn_splits_handler, 1, 0 },
{ NULL, NULL, 0, 0 },
};
static gboolean
gnc_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)
{
Transaction* trn = NULL;
xmlNodePtr tree = (xmlNodePtr)data_for_children;
gxpf_data* gdata = (gxpf_data*)global_data;
if (parent_data)
{
return TRUE;
}
/* OK. For some messed up reason this is getting called again with a
NULL tag. So we ignore those cases */
if (!tag)
{
return TRUE;
}
g_return_val_if_fail (tree, FALSE);
trn = dom_tree_to_transaction (tree,
static_cast<QofBook*> (gdata->bookdata));
if (trn != NULL)
{
gdata->cb (tag, gdata->parsedata, trn);
}
xmlFreeNode (tree);
return trn != NULL;
}
Transaction*
dom_tree_to_transaction (xmlNodePtr node, QofBook* book)
{
Transaction* trn;
gboolean successful;
struct trans_pdata pdata;
g_return_val_if_fail (node, NULL);
g_return_val_if_fail (book, NULL);
trn = xaccMallocTransaction (book);
g_return_val_if_fail (trn, NULL);
xaccTransBeginEdit (trn);
pdata.trans = trn;
pdata.book = book;
successful = dom_tree_generic_parse (node, trn_dom_handlers, &pdata);
xaccTransCommitEdit (trn);
if (!successful)
{
xmlElemDump (stdout, NULL, node);
xaccTransBeginEdit (trn);
xaccTransDestroy (trn);
xaccTransCommitEdit (trn);
trn = NULL;
}
return trn;
}
sixtp*
gnc_transaction_sixtp_parser_create (void)
{
return sixtp_dom_parser_new (gnc_transaction_end_handler, NULL, NULL);
}