GnuCash 2.4.99
gnc-transaction-xml-v2.c
00001 /********************************************************************
00002  * gnc-transactions-xml-v2.c -- xml routines for transactions       *
00003  * Copyright (C) 2001 Rob Browning                                  *
00004  * Copyright (C) 2002 Linas Vepstas <linas@linas.org>               *
00005  *                                                                  *
00006  * This program is free software; you can redistribute it and/or    *
00007  * modify it under the terms of the GNU General Public License as   *
00008  * published by the Free Software Foundation; either version 2 of   *
00009  * the License, or (at your option) any later version.              *
00010  *                                                                  *
00011  * This program is distributed in the hope that it will be useful,  *
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
00014  * GNU General Public License for more details.                     *
00015  *                                                                  *
00016  * You should have received a copy of the GNU General Public License*
00017  * along with this program; if not, contact:                        *
00018  *                                                                  *
00019  * Free Software Foundation           Voice:  +1-617-542-5942       *
00020  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
00021  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
00022  *                                                                  *
00023  *******************************************************************/
00024 
00025 #include "config.h"
00026 
00027 #include <glib.h>
00028 #include <string.h>
00029 
00030 #include "gnc-xml-helper.h"
00031 
00032 #include "sixtp.h"
00033 #include "sixtp-utils.h"
00034 #include "sixtp-parsers.h"
00035 #include "sixtp-utils.h"
00036 #include "sixtp-dom-parsers.h"
00037 #include "sixtp-dom-generators.h"
00038 
00039 #include "gnc-xml.h"
00040 
00041 #include "io-gncxml-gen.h"
00042 
00043 #include "sixtp-dom-parsers.h"
00044 #include "AccountP.h"
00045 #include "Transaction.h"
00046 #include "TransactionP.h"
00047 #include "gnc-lot.h"
00048 #include "gnc-lot-p.h"
00049 
00050 const gchar *transaction_version_string = "2.0.0";
00051 
00052 static void
00053 add_gnc_num(xmlNodePtr node, const gchar *tag, gnc_numeric num)
00054 {
00055     xmlAddChild(node, gnc_numeric_to_dom_tree(tag, &num));
00056 }
00057 
00058 static void
00059 add_timespec(xmlNodePtr node, const gchar *tag, Timespec tms, gboolean always)
00060 {
00061     if (always || !((tms.tv_sec == 0) && (tms.tv_nsec == 0)))
00062     {
00063         xmlAddChild(node, timespec_to_dom_tree(tag, &tms));
00064     }
00065 }
00066 
00067 static xmlNodePtr
00068 split_to_dom_tree(const gchar *tag, Split *spl)
00069 {
00070     xmlNodePtr ret;
00071 
00072     ret = xmlNewNode(NULL, BAD_CAST tag);
00073 
00074     xmlAddChild(ret, guid_to_dom_tree("split:id", xaccSplitGetGUID(spl)));
00075 
00076     {
00077         const char *memo = xaccSplitGetMemo(spl);
00078 
00079         if (memo && safe_strcmp(memo, "") != 0)
00080         {
00081             xmlNewTextChild(ret, NULL, BAD_CAST "split:memo", (xmlChar*)memo);
00082         }
00083     }
00084 
00085     {
00086         const char *action = xaccSplitGetAction(spl);
00087 
00088         if (action && safe_strcmp(action, "") != 0)
00089         {
00090             xmlNewTextChild(ret, NULL, BAD_CAST "split:action", (xmlChar*)action);
00091         }
00092     }
00093 
00094     {
00095         char tmp[2];
00096 
00097         tmp[0] = xaccSplitGetReconcile(spl);
00098         tmp[1] = '\0';
00099 
00100         xmlNewTextChild(ret, NULL, BAD_CAST "split:reconciled-state", (xmlChar*)tmp);
00101     }
00102 
00103     add_timespec(ret, "split:reconcile-date",
00104                  xaccSplitRetDateReconciledTS(spl), FALSE);
00105 
00106     add_gnc_num(ret, "split:value", xaccSplitGetValue(spl));
00107 
00108     add_gnc_num(ret, "split:quantity", xaccSplitGetAmount(spl));
00109 
00110     {
00111         Account * account = xaccSplitGetAccount (spl);
00112 
00113         xmlAddChild (ret, guid_to_dom_tree("split:account",
00114                                            xaccAccountGetGUID (account)));
00115     }
00116     {
00117         GNCLot * lot = xaccSplitGetLot (spl);
00118 
00119         if (lot)
00120         {
00121             xmlAddChild (ret, guid_to_dom_tree("split:lot",
00122                                                gnc_lot_get_guid(lot)));
00123         }
00124     }
00125     {
00126         xmlNodePtr kvpnode = kvp_frame_to_dom_tree("split:slots",
00127                              xaccSplitGetSlots(spl));
00128         if (kvpnode)
00129         {
00130             xmlAddChild(ret, kvpnode);
00131         }
00132     }
00133 
00134     return ret;
00135 }
00136 
00137 static void
00138 add_trans_splits(xmlNodePtr node, Transaction *trn)
00139 {
00140     GList *n;
00141     xmlNodePtr toaddto;
00142 
00143     toaddto = xmlNewChild(node, NULL, BAD_CAST "trn:splits", NULL);
00144 
00145     for (n = xaccTransGetSplitList(trn); n; n = n->next)
00146     {
00147         Split *s = n->data;
00148         xmlAddChild(toaddto, split_to_dom_tree("trn:split", s));
00149     }
00150 }
00151 
00152 xmlNodePtr
00153 gnc_transaction_dom_tree_create(Transaction *trn)
00154 {
00155     xmlNodePtr ret;
00156 
00157     ret = xmlNewNode(NULL, BAD_CAST "gnc:transaction");
00158 
00159     xmlSetProp(ret, BAD_CAST "version", BAD_CAST transaction_version_string);
00160 
00161     xmlAddChild(ret, guid_to_dom_tree("trn:id", xaccTransGetGUID(trn)));
00162 
00163     xmlAddChild(ret, commodity_ref_to_dom_tree("trn:currency",
00164                 xaccTransGetCurrency(trn)));
00165 
00166     if (xaccTransGetNum(trn) && (safe_strcmp(xaccTransGetNum(trn), "") != 0))
00167     {
00168         xmlNewTextChild(ret, NULL, BAD_CAST "trn:num", (xmlChar*)xaccTransGetNum(trn));
00169     }
00170 
00171     add_timespec(ret, "trn:date-posted", xaccTransRetDatePostedTS(trn), TRUE);
00172 
00173     add_timespec(ret, "trn:date-entered",
00174                  xaccTransRetDateEnteredTS(trn), TRUE);
00175 
00176     if (xaccTransGetDescription(trn))
00177     {
00178         xmlNewTextChild(ret, NULL, BAD_CAST "trn:description",
00179                         (xmlChar*)xaccTransGetDescription(trn));
00180     }
00181 
00182     {
00183         xmlNodePtr kvpnode = kvp_frame_to_dom_tree("trn:slots",
00184                              xaccTransGetSlots(trn));
00185         if (kvpnode)
00186         {
00187             xmlAddChild(ret, kvpnode);
00188         }
00189     }
00190 
00191     add_trans_splits(ret, trn);
00192 
00193     return ret;
00194 }
00195 
00196 /***********************************************************************/
00197 
00198 struct split_pdata
00199 {
00200     Split *split;
00201     QofBook *book;
00202 };
00203 
00204 static inline gboolean
00205 set_spl_string(xmlNodePtr node, Split *spl,
00206                void (*func)(Split *spl, const char *txt))
00207 {
00208     gchar *tmp = dom_tree_to_text(node);
00209     g_return_val_if_fail(tmp, FALSE);
00210 
00211     func(spl, tmp);
00212 
00213     g_free(tmp);
00214 
00215     return TRUE;
00216 }
00217 
00218 static inline gboolean
00219 set_spl_gnc_num(xmlNodePtr node, Split* spl,
00220                 void (*func)(Split *spl, gnc_numeric gn))
00221 {
00222     gnc_numeric *num = dom_tree_to_gnc_numeric(node);
00223     g_return_val_if_fail(num, FALSE);
00224 
00225     func(spl, *num);
00226 
00227     g_free(num);
00228 
00229     return FALSE;
00230 }
00231 
00232 static gboolean
00233 spl_id_handler(xmlNodePtr node, gpointer data)
00234 {
00235     struct split_pdata *pdata = data;
00236     GncGUID *tmp = dom_tree_to_guid(node);
00237     g_return_val_if_fail(tmp, FALSE);
00238 
00239     xaccSplitSetGUID(pdata->split, tmp);
00240 
00241     g_free(tmp);
00242     return TRUE;
00243 }
00244 
00245 static gboolean
00246 spl_memo_handler(xmlNodePtr node, gpointer data)
00247 {
00248     struct split_pdata *pdata = data;
00249     return set_spl_string(node, pdata->split, xaccSplitSetMemo);
00250 }
00251 
00252 static gboolean
00253 spl_action_handler(xmlNodePtr node, gpointer data)
00254 {
00255     struct split_pdata *pdata = data;
00256     return set_spl_string(node, pdata->split, xaccSplitSetAction);
00257 }
00258 
00259 static gboolean
00260 spl_reconciled_state_handler(xmlNodePtr node, gpointer data)
00261 {
00262     struct split_pdata *pdata = data;
00263     gchar *tmp = dom_tree_to_text(node);
00264     g_return_val_if_fail(tmp, FALSE);
00265 
00266     xaccSplitSetReconcile(pdata->split, tmp[0]);
00267 
00268     g_free(tmp);
00269 
00270     return TRUE;
00271 }
00272 
00273 static gboolean
00274 spl_reconcile_date_handler(xmlNodePtr node, gpointer data)
00275 {
00276     struct split_pdata *pdata = data;
00277     Timespec ts;
00278 
00279     ts = dom_tree_to_timespec(node);
00280     if (!dom_tree_valid_timespec(&ts, node->name)) return FALSE;
00281 
00282     xaccSplitSetDateReconciledTS(pdata->split, &ts);
00283 
00284     return TRUE;
00285 }
00286 
00287 static gboolean
00288 spl_value_handler(xmlNodePtr node, gpointer data)
00289 {
00290     struct split_pdata *pdata = data;
00291     return set_spl_gnc_num(node, pdata->split, xaccSplitSetValue);
00292 }
00293 
00294 static gboolean
00295 spl_quantity_handler(xmlNodePtr node, gpointer data)
00296 {
00297     struct split_pdata *pdata = data;
00298     return set_spl_gnc_num(node, pdata->split, xaccSplitSetAmount);
00299 }
00300 
00301 gboolean gnc_transaction_xml_v2_testing = FALSE;
00302 
00303 static gboolean
00304 spl_account_handler(xmlNodePtr node, gpointer data)
00305 {
00306     struct split_pdata *pdata = data;
00307     GncGUID *id = dom_tree_to_guid(node);
00308     Account *account;
00309 
00310     g_return_val_if_fail(id, FALSE);
00311 
00312     account = xaccAccountLookup (id, pdata->book);
00313     if (!account && gnc_transaction_xml_v2_testing &&
00314             !guid_equal (id, guid_null ()))
00315     {
00316         account = xaccMallocAccount (pdata->book);
00317         xaccAccountSetGUID (account, id);
00318         xaccAccountSetCommoditySCU (account,
00319                                     xaccSplitGetAmount (pdata->split).denom);
00320     }
00321 
00322     xaccAccountInsertSplit (account, pdata->split);
00323 
00324     g_free(id);
00325 
00326     return TRUE;
00327 }
00328 
00329 static gboolean
00330 spl_lot_handler(xmlNodePtr node, gpointer data)
00331 {
00332     struct split_pdata *pdata = data;
00333     GncGUID *id = dom_tree_to_guid(node);
00334     GNCLot *lot;
00335 
00336     g_return_val_if_fail(id, FALSE);
00337 
00338     lot = gnc_lot_lookup (id, pdata->book);
00339     if (!lot && gnc_transaction_xml_v2_testing &&
00340             !guid_equal (id, guid_null ()))
00341     {
00342         lot = gnc_lot_new (pdata->book);
00343         gnc_lot_set_guid (lot, *id);
00344     }
00345 
00346     gnc_lot_add_split (lot, pdata->split);
00347 
00348     g_free(id);
00349 
00350     return TRUE;
00351 }
00352 
00353 static gboolean
00354 spl_slots_handler(xmlNodePtr node, gpointer data)
00355 {
00356     struct split_pdata *pdata = data;
00357     gboolean successful;
00358 
00359     successful = dom_tree_to_kvp_frame_given(node,
00360                  xaccSplitGetSlots (pdata->split));
00361     g_return_val_if_fail(successful, FALSE);
00362 
00363     return TRUE;
00364 }
00365 
00366 struct dom_tree_handler spl_dom_handlers[] =
00367 {
00368     { "split:id", spl_id_handler, 1, 0 },
00369     { "split:memo", spl_memo_handler, 0, 0 },
00370     { "split:action", spl_action_handler, 0, 0 },
00371     { "split:reconciled-state", spl_reconciled_state_handler, 1, 0 },
00372     { "split:reconcile-date", spl_reconcile_date_handler, 0, 0 },
00373     { "split:value", spl_value_handler, 1, 0 },
00374     { "split:quantity", spl_quantity_handler, 1, 0 },
00375     { "split:account", spl_account_handler, 1, 0 },
00376     { "split:lot", spl_lot_handler, 0, 0 },
00377     { "split:slots", spl_slots_handler, 0, 0 },
00378     { NULL, NULL, 0, 0 },
00379 };
00380 
00381 static Split*
00382 dom_tree_to_split(xmlNodePtr node, QofBook *book)
00383 {
00384     struct split_pdata pdata;
00385     Split *ret;
00386 
00387     g_return_val_if_fail (book, NULL);
00388 
00389     ret = xaccMallocSplit(book);
00390     g_return_val_if_fail(ret, NULL);
00391 
00392     pdata.split = ret;
00393     pdata.book = book;
00394 
00395     /* this isn't going to work in a testing setup */
00396     if (dom_tree_generic_parse(node, spl_dom_handlers, &pdata))
00397     {
00398         return ret;
00399     }
00400     else
00401     {
00402         xaccSplitDestroy(ret);
00403         return NULL;
00404     }
00405 }
00406 
00407 /***********************************************************************/
00408 
00409 struct trans_pdata
00410 {
00411     Transaction *trans;
00412     QofBook *book;
00413 };
00414 
00415 static inline gboolean
00416 set_tran_string(xmlNodePtr node, Transaction *trn,
00417                 void (*func)(Transaction *trn, const char *txt))
00418 {
00419     gchar *tmp;
00420 
00421     tmp = dom_tree_to_text(node);
00422 
00423     g_return_val_if_fail(tmp, FALSE);
00424 
00425     func(trn, tmp);
00426 
00427     g_free(tmp);
00428 
00429     return TRUE;
00430 }
00431 
00432 static inline gboolean
00433 set_tran_date(xmlNodePtr node, Transaction *trn,
00434               void (*func)(Transaction *trn, const Timespec *tm))
00435 {
00436     Timespec tm;
00437 
00438     tm = dom_tree_to_timespec(node);
00439 
00440     if (!dom_tree_valid_timespec(&tm, node->name)) return FALSE;
00441 
00442     func(trn, &tm);
00443 
00444     return TRUE;
00445 }
00446 
00447 static gboolean
00448 trn_id_handler(xmlNodePtr node, gpointer trans_pdata)
00449 {
00450     struct trans_pdata *pdata = trans_pdata;
00451     Transaction *trn = pdata->trans;
00452     GncGUID *tmp = dom_tree_to_guid(node);
00453 
00454     g_return_val_if_fail(tmp, FALSE);
00455 
00456     xaccTransSetGUID((Transaction*)trn, tmp);
00457 
00458     g_free(tmp);
00459 
00460     return TRUE;
00461 }
00462 
00463 static gboolean
00464 trn_currency_handler(xmlNodePtr node, gpointer trans_pdata)
00465 {
00466     struct trans_pdata *pdata = trans_pdata;
00467     Transaction *trn = pdata->trans;
00468     gnc_commodity *ref;
00469 
00470     ref = dom_tree_to_commodity_ref(node, pdata->book);
00471     xaccTransSetCurrency(trn, ref);
00472 
00473     return TRUE;
00474 }
00475 
00476 static gboolean
00477 trn_num_handler(xmlNodePtr node, gpointer trans_pdata)
00478 {
00479     struct trans_pdata *pdata = trans_pdata;
00480     Transaction *trn = pdata->trans;
00481 
00482     return set_tran_string(node, trn, xaccTransSetNum);
00483 }
00484 
00485 static gboolean
00486 trn_date_posted_handler(xmlNodePtr node, gpointer trans_pdata)
00487 {
00488     struct trans_pdata *pdata = trans_pdata;
00489     Transaction *trn = pdata->trans;
00490 
00491     return set_tran_date(node, trn, xaccTransSetDatePostedTS);
00492 }
00493 
00494 static gboolean
00495 trn_date_entered_handler(xmlNodePtr node, gpointer trans_pdata)
00496 {
00497     struct trans_pdata *pdata = trans_pdata;
00498     Transaction *trn = pdata->trans;
00499 
00500     return set_tran_date(node, trn, xaccTransSetDateEnteredTS);
00501 }
00502 
00503 static gboolean
00504 trn_description_handler(xmlNodePtr node, gpointer trans_pdata)
00505 {
00506     struct trans_pdata *pdata = trans_pdata;
00507     Transaction *trn = pdata->trans;
00508 
00509     return set_tran_string(node, trn, xaccTransSetDescription);
00510 }
00511 
00512 static gboolean
00513 trn_slots_handler(xmlNodePtr node, gpointer trans_pdata)
00514 {
00515     struct trans_pdata *pdata = trans_pdata;
00516     Transaction *trn = pdata->trans;
00517     gboolean successful;
00518 
00519     successful = dom_tree_to_kvp_frame_given(node, xaccTransGetSlots(trn));
00520 
00521     g_return_val_if_fail(successful, FALSE);
00522 
00523     return TRUE;
00524 }
00525 
00526 static gboolean
00527 trn_splits_handler(xmlNodePtr node, gpointer trans_pdata)
00528 {
00529     struct trans_pdata *pdata = trans_pdata;
00530     Transaction *trn = pdata->trans;
00531     xmlNodePtr mark;
00532 
00533     g_return_val_if_fail(node, FALSE);
00534     g_return_val_if_fail(node->xmlChildrenNode, FALSE);
00535 
00536     for (mark = node->xmlChildrenNode; mark; mark = mark->next)
00537     {
00538         Split *spl;
00539 
00540         if (safe_strcmp("text", (char*)mark->name) == 0)
00541             continue;
00542 
00543         if (safe_strcmp("trn:split", (char*)mark->name))
00544         {
00545             return FALSE;
00546         }
00547 
00548         spl = dom_tree_to_split(mark, pdata->book);
00549 
00550         if (spl)
00551         {
00552             xaccTransAppendSplit(trn, spl);
00553         }
00554         else
00555         {
00556             return FALSE;
00557         }
00558     }
00559     return TRUE;
00560 }
00561 
00562 struct dom_tree_handler trn_dom_handlers[] =
00563 {
00564     { "trn:id", trn_id_handler, 1, 0 },
00565     { "trn:currency", trn_currency_handler, 0, 0},
00566     { "trn:num", trn_num_handler, 0, 0 },
00567     { "trn:date-posted", trn_date_posted_handler, 1, 0 },
00568     { "trn:date-entered", trn_date_entered_handler, 1, 0 },
00569     { "trn:description", trn_description_handler, 0, 0 },
00570     { "trn:slots", trn_slots_handler, 0, 0 },
00571     { "trn:splits", trn_splits_handler, 1, 0 },
00572     { NULL, NULL, 0, 0 },
00573 };
00574 
00575 static gboolean
00576 gnc_transaction_end_handler(gpointer data_for_children,
00577                             GSList* data_from_children, GSList* sibling_data,
00578                             gpointer parent_data, gpointer global_data,
00579                             gpointer *result, const gchar *tag)
00580 {
00581     Transaction *trn = NULL;
00582     gboolean successful = FALSE;
00583     xmlNodePtr tree = (xmlNodePtr)data_for_children;
00584     gxpf_data *gdata = (gxpf_data*)global_data;
00585 
00586     if (parent_data)
00587     {
00588         return TRUE;
00589     }
00590 
00591     /* OK.  For some messed up reason this is getting called again with a
00592        NULL tag.  So we ignore those cases */
00593     if (!tag)
00594     {
00595         return TRUE;
00596     }
00597 
00598     g_return_val_if_fail(tree, FALSE);
00599 
00600     trn = dom_tree_to_transaction(tree, gdata->bookdata);
00601     if (trn != NULL)
00602     {
00603         gdata->cb(tag, gdata->parsedata, trn);
00604         successful = TRUE;
00605     }
00606 
00607     xmlFreeNode(tree);
00608 
00609     return trn != NULL;
00610 }
00611 
00612 Transaction *
00613 dom_tree_to_transaction( xmlNodePtr node, QofBook *book )
00614 {
00615     Transaction *trn;
00616     gboolean successful;
00617     struct trans_pdata pdata;
00618 
00619     g_return_val_if_fail(node, NULL);
00620     g_return_val_if_fail(book, NULL);
00621 
00622     trn = xaccMallocTransaction(book);
00623     g_return_val_if_fail(trn, NULL);
00624     xaccTransBeginEdit(trn);
00625 
00626     pdata.trans = trn;
00627     pdata.book = book;
00628 
00629     successful = dom_tree_generic_parse(node, trn_dom_handlers, &pdata);
00630 
00631     xaccTransCommitEdit(trn);
00632 
00633     if ( !successful )
00634     {
00635         xmlElemDump(stdout, NULL, node);
00636         xaccTransBeginEdit(trn);
00637         xaccTransDestroy(trn);
00638         xaccTransCommitEdit(trn);
00639         trn = NULL;
00640     }
00641 
00642     return trn;
00643 }
00644 
00645 sixtp*
00646 gnc_transaction_sixtp_parser_create(void)
00647 {
00648     return sixtp_dom_parser_new(gnc_transaction_end_handler, NULL, NULL);
00649 }
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines