GnuCash  5.6-150-g038405b370+
gnc-customer-xml-v2.cpp
1 /********************************************************************\
2  * gnc-customer-xml-v2.c -- customer xml i/o implementation *
3  * *
4  * Copyright (C) 2002 Derek Atkins <warlord@MIT.EDU> *
5  * *
6  * This program is free software; you can redistribute it and/or *
7  * modify it under the terms of the GNU General Public License as *
8  * published by the Free Software Foundation; either version 2 of *
9  * the License, or (at your option) any later version. *
10  * *
11  * This program is distributed in the hope that it will be useful, *
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14  * GNU General Public License for more details. *
15  * *
16  * You should have received a copy of the GNU General Public License*
17  * along with this program; if not, contact: *
18  * *
19  * Free Software Foundation Voice: +1-617-542-5942 *
20  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
21  * Boston, MA 02110-1301, USA gnu@gnu.org *
22  * *
23 \********************************************************************/
24 #include <glib.h>
25 
26 #include <config.h>
27 #include <stdlib.h>
28 #include <string.h>
29 
30 #include "gncBillTermP.h"
31 #include "gncCustomerP.h"
32 #include "gncTaxTableP.h"
33 
34 #include "gnc-xml-helper.h"
35 #include "gnc-customer-xml-v2.h"
36 #include "gnc-address-xml-v2.h"
37 #include "gnc-bill-term-xml-v2.h"
38 #include "sixtp.h"
39 #include "sixtp-utils.h"
40 #include "sixtp-parsers.h"
41 #include "sixtp-utils.h"
42 #include "sixtp-dom-parsers.h"
43 #include "sixtp-dom-generators.h"
44 
45 #include "gnc-xml.h"
46 #include "io-gncxml-gen.h"
47 #include "io-gncxml-v2.h"
48 
49 #include "xml-helpers.h"
50 
51 #define _GNC_MOD_NAME GNC_ID_CUSTOMER
52 
53 static QofLogModule log_module = GNC_MOD_IO;
54 
55 const gchar* customer_version_string = "2.0.0";
56 
57 /* ids */
58 #define gnc_customer_string "gnc:GncCustomer"
59 #define cust_name_string "cust:name"
60 #define cust_guid_string "cust:guid"
61 #define cust_id_string "cust:id"
62 #define cust_addr_string "cust:addr"
63 #define cust_shipaddr_string "cust:shipaddr"
64 #define cust_notes_string "cust:notes"
65 #define cust_terms_string "cust:terms"
66 #define cust_taxincluded_string "cust:taxincluded"
67 #define cust_active_string "cust:active"
68 #define cust_discount_string "cust:discount"
69 #define cust_credit_string "cust:credit"
70 #define cust_currency_string "cust:currency"
71 #define cust_taxtable_string "cust:taxtable"
72 #define cust_taxtableoverride_string "cust:use-tt"
73 #define cust_slots_string "cust:slots"
74 
75 static xmlNodePtr
76 customer_dom_tree_create (GncCustomer* cust)
77 {
78  xmlNodePtr ret;
79  gnc_numeric num;
80  GncBillTerm* term;
81  GncTaxTable* taxtable;
82 
83  ret = xmlNewNode (NULL, BAD_CAST gnc_customer_string);
84  xmlSetProp (ret, BAD_CAST "version", BAD_CAST customer_version_string);
85 
86  xmlAddChild (ret, guid_to_dom_tree (cust_guid_string,
87  qof_instance_get_guid (QOF_INSTANCE (cust))));
88 
89  xmlAddChild (ret, text_to_dom_tree (cust_name_string,
90  gncCustomerGetName (cust)));
91 
92  xmlAddChild (ret, text_to_dom_tree (cust_id_string,
93  gncCustomerGetID (cust)));
94 
95  xmlAddChild (ret, gnc_address_to_dom_tree (cust_addr_string,
96  gncCustomerGetAddr (cust)));
97 
98  xmlAddChild (ret, gnc_address_to_dom_tree (cust_shipaddr_string,
99  gncCustomerGetShipAddr (cust)));
100 
101  maybe_add_string (ret, cust_notes_string, gncCustomerGetNotes (cust));
102 
103  term = gncCustomerGetTerms (cust);
104  if (term)
105  xmlAddChild (ret, guid_to_dom_tree (cust_terms_string,
106  qof_instance_get_guid (QOF_INSTANCE (term))));
107 
108  xmlAddChild (ret, text_to_dom_tree (cust_taxincluded_string,
109  gncTaxIncludedTypeToString (
110  gncCustomerGetTaxIncluded (cust))));
111 
112  xmlAddChild (ret, int_to_dom_tree (cust_active_string,
113  gncCustomerGetActive (cust)));
114 
115  num = gncCustomerGetDiscount (cust);
116  xmlAddChild (ret, gnc_numeric_to_dom_tree (cust_discount_string, &num));
117 
118  num = gncCustomerGetCredit (cust);
119  xmlAddChild (ret, gnc_numeric_to_dom_tree (cust_credit_string, &num));
120 
121  xmlAddChild
122  (ret,
123  commodity_ref_to_dom_tree (cust_currency_string,
124  gncCustomerGetCurrency (cust)));
125 
126  xmlAddChild (ret, int_to_dom_tree (cust_taxtableoverride_string,
127  gncCustomerGetTaxTableOverride (cust)));
128  taxtable = gncCustomerGetTaxTable (cust);
129  if (taxtable)
130  xmlAddChild (ret, guid_to_dom_tree (cust_taxtable_string,
131  qof_instance_get_guid (QOF_INSTANCE (taxtable))));
132 
133  /* xmlAddChild won't do anything with a NULL, so tests are superfluous. */
134  xmlAddChild (ret, qof_instance_slots_to_dom_tree (cust_slots_string,
135  QOF_INSTANCE (cust)));
136 
137  return ret;
138 }
139 
140 /***********************************************************************/
141 
143 {
144  GncCustomer* customer;
145  QofBook* book;
146 };
147 
148 static gboolean
149 set_string (xmlNodePtr node, GncCustomer* cust,
150  void (*func) (GncCustomer* cust, const char* txt))
151 {
152  char* txt = dom_tree_to_text (node);
153  g_return_val_if_fail (txt, FALSE);
154 
155  func (cust, txt);
156 
157  g_free (txt);
158 
159  return TRUE;
160 }
161 
162 static gboolean
163 set_boolean (xmlNodePtr node, GncCustomer* cust,
164  void (*func) (GncCustomer* cust, gboolean b))
165 {
166  gint64 val;
167  gboolean ret;
168 
169  ret = dom_tree_to_integer (node, &val);
170  if (ret)
171  func (cust, (gboolean)val);
172 
173  return ret;
174 }
175 
176 static gboolean
177 customer_name_handler (xmlNodePtr node, gpointer cust_pdata)
178 {
179  struct customer_pdata* pdata = static_cast<decltype (pdata)> (cust_pdata);
180 
181  return set_string (node, pdata->customer, gncCustomerSetName);
182 }
183 
184 static gboolean
185 customer_guid_handler (xmlNodePtr node, gpointer cust_pdata)
186 {
187  struct customer_pdata* pdata = static_cast<decltype (pdata)> (cust_pdata);
188  GncGUID* guid;
189  GncCustomer* cust;
190 
191  guid = dom_tree_to_guid (node);
192  g_return_val_if_fail (guid, FALSE);
193  cust = gncCustomerLookup (pdata->book, guid);
194  if (cust)
195  {
196  gncCustomerDestroy (pdata->customer);
197  pdata->customer = cust;
198  gncCustomerBeginEdit (cust);
199  }
200  else
201  {
202  gncCustomerSetGUID (pdata->customer, guid);
203  }
204 
205  guid_free (guid);
206 
207  return TRUE;
208 }
209 
210 static gboolean
211 customer_id_handler (xmlNodePtr node, gpointer cust_pdata)
212 {
213  struct customer_pdata* pdata = static_cast<decltype (pdata)> (cust_pdata);
214 
215  return set_string (node, pdata->customer, gncCustomerSetID);
216 }
217 
218 static gboolean
219 customer_notes_handler (xmlNodePtr node, gpointer cust_pdata)
220 {
221  struct customer_pdata* pdata = static_cast<decltype (pdata)> (cust_pdata);
222 
223  return set_string (node, pdata->customer, gncCustomerSetNotes);
224 }
225 
226 static gboolean
227 customer_terms_handler (xmlNodePtr node, gpointer cust_pdata)
228 {
229  struct customer_pdata* pdata = static_cast<decltype (pdata)> (cust_pdata);
230  GncGUID* guid;
231  GncBillTerm* term;
232 
233  guid = dom_tree_to_guid (node);
234  g_return_val_if_fail (guid, FALSE);
235  term = gnc_billterm_xml_find_or_create (pdata->book, guid);
236  g_assert (term);
237  guid_free (guid);
238  gncCustomerSetTerms (pdata->customer, term);
239 
240  return TRUE;
241 }
242 
243 static gboolean
244 customer_addr_handler (xmlNodePtr node, gpointer cust_pdata)
245 {
246  struct customer_pdata* pdata = static_cast<decltype (pdata)> (cust_pdata);
247 
248  return gnc_dom_tree_to_address (node, gncCustomerGetAddr (pdata->customer));
249 }
250 
251 static gboolean
252 customer_shipaddr_handler (xmlNodePtr node, gpointer cust_pdata)
253 {
254  struct customer_pdata* pdata = static_cast<decltype (pdata)> (cust_pdata);
255 
256  return gnc_dom_tree_to_address (node,
257  gncCustomerGetShipAddr (pdata->customer));
258 }
259 
260 
261 static gboolean
262 customer_taxincluded_handler (xmlNodePtr node, gpointer cust_pdata)
263 {
264  struct customer_pdata* pdata = static_cast<decltype (pdata)> (cust_pdata);
265  GncTaxIncluded type;
266  char* str;
267  gboolean ret;
268 
269  str = dom_tree_to_text (node);
270  g_return_val_if_fail (str, FALSE);
271 
272  ret = gncTaxIncludedStringToType (str, &type);
273  g_free (str);
274 
275  if (ret)
276  gncCustomerSetTaxIncluded (pdata->customer, type);
277 
278  return ret;
279 }
280 
281 static gboolean
282 customer_active_handler (xmlNodePtr node, gpointer cust_pdata)
283 {
284  struct customer_pdata* pdata = static_cast<decltype (pdata)> (cust_pdata);
285  return set_boolean (node, pdata->customer, gncCustomerSetActive);
286 }
287 
288 static gboolean
289 customer_discount_handler (xmlNodePtr node, gpointer cust_pdata)
290 {
291  struct customer_pdata* pdata = static_cast<decltype (pdata)> (cust_pdata);
292 
293  gncCustomerSetDiscount (pdata->customer, dom_tree_to_gnc_numeric (node));
294  return TRUE;
295 }
296 
297 static gboolean
298 customer_credit_handler (xmlNodePtr node, gpointer cust_pdata)
299 {
300  struct customer_pdata* pdata = static_cast<decltype (pdata)> (cust_pdata);
301 
302  gncCustomerSetCredit (pdata->customer, dom_tree_to_gnc_numeric (node));
303  return TRUE;
304 }
305 
306 static gboolean
307 customer_currency_handler (xmlNodePtr node, gpointer customer_pdata)
308 {
309  struct customer_pdata* pdata = static_cast<decltype (pdata)> (customer_pdata);
310  gnc_commodity* com;
311 
312  com = dom_tree_to_commodity_ref (node, pdata->book);
313  g_return_val_if_fail (com, FALSE);
314 
315  gncCustomerSetCurrency (pdata->customer, com);
316 
317  return TRUE;
318 }
319 
320 static gboolean
321 customer_taxtable_handler (xmlNodePtr node, gpointer cust_pdata)
322 {
323  struct customer_pdata* pdata = static_cast<decltype (pdata)> (cust_pdata);
324  GncGUID* guid;
325  GncTaxTable* taxtable;
326 
327  guid = dom_tree_to_guid (node);
328  g_return_val_if_fail (guid, FALSE);
329  taxtable = gncTaxTableLookup (pdata->book, guid);
330  if (!taxtable)
331  {
332  taxtable = gncTaxTableCreate (pdata->book);
333  gncTaxTableBeginEdit (taxtable);
334  gncTaxTableSetGUID (taxtable, guid);
335  gncTaxTableCommitEdit (taxtable);
336  }
337  else
338  gncTaxTableDecRef (taxtable);
339 
340  gncCustomerSetTaxTable (pdata->customer, taxtable);
341  guid_free (guid);
342  return TRUE;
343 }
344 
345 static gboolean
346 customer_taxtableoverride_handler (xmlNodePtr node, gpointer cust_pdata)
347 {
348  struct customer_pdata* pdata = static_cast<decltype (pdata)> (cust_pdata);
349  return set_boolean (node, pdata->customer, gncCustomerSetTaxTableOverride);
350 }
351 
352 static gboolean
353 customer_slots_handler (xmlNodePtr node, gpointer cust_pdata)
354 {
355  struct customer_pdata* pdata = static_cast<decltype (pdata)> (cust_pdata);
356  return dom_tree_create_instance_slots (node, QOF_INSTANCE (pdata->customer));
357 }
358 
359 static struct dom_tree_handler customer_handlers_v2[] =
360 {
361  { cust_name_string, customer_name_handler, 1, 0 },
362  { cust_guid_string, customer_guid_handler, 1, 0 },
363  { cust_id_string, customer_id_handler, 1, 0 },
364  { cust_addr_string, customer_addr_handler, 1, 0 },
365  { cust_shipaddr_string, customer_shipaddr_handler, 1, 0 },
366  { cust_notes_string, customer_notes_handler, 0, 0 },
367  { cust_terms_string, customer_terms_handler, 0, 0 },
368  { cust_taxincluded_string, customer_taxincluded_handler, 1, 0 },
369  { cust_active_string, customer_active_handler, 1, 0 },
370  { cust_discount_string, customer_discount_handler, 1, 0 },
371  { cust_credit_string, customer_credit_handler, 1, 0 },
372  { cust_currency_string, customer_currency_handler, 0, 0 }, /* XXX */
373  { "cust:commodity", customer_currency_handler, 0, 0 }, /* XXX */
374  { cust_taxtable_string, customer_taxtable_handler, 0, 0 },
375  { cust_taxtableoverride_string, customer_taxtableoverride_handler, 0, 0 },
376  { cust_slots_string, customer_slots_handler, 0, 0 },
377  { NULL, 0, 0, 0 }
378 };
379 
380 static GncCustomer*
381 dom_tree_to_customer (xmlNodePtr node, QofBook* book)
382 {
383  struct customer_pdata cust_pdata;
384  gboolean successful;
385 
386  cust_pdata.customer = gncCustomerCreate (book);
387  cust_pdata.book = book;
388  gncCustomerBeginEdit (cust_pdata.customer);
389 
390  successful = dom_tree_generic_parse (node, customer_handlers_v2,
391  &cust_pdata);
392 
393  if (successful)
394  gncCustomerCommitEdit (cust_pdata.customer);
395  else
396  {
397  PERR ("failed to parse customer tree");
398  gncCustomerDestroy (cust_pdata.customer);
399  cust_pdata.customer = NULL;
400  }
401 
402  return cust_pdata.customer;
403 }
404 
405 static gboolean
406 gnc_customer_end_handler (gpointer data_for_children,
407  GSList* data_from_children, GSList* sibling_data,
408  gpointer parent_data, gpointer global_data,
409  gpointer* result, const gchar* tag)
410 {
411  GncCustomer* cust;
412  xmlNodePtr tree = (xmlNodePtr)data_for_children;
413  gxpf_data* gdata = (gxpf_data*)global_data;
414  QofBook* book = static_cast<decltype (book)> (gdata->bookdata);
415 
416 
417  if (parent_data)
418  {
419  return TRUE;
420  }
421 
422  /* OK. For some messed up reason this is getting called again with a
423  NULL tag. So we ignore those cases */
424  if (!tag)
425  {
426  return TRUE;
427  }
428 
429  g_return_val_if_fail (tree, FALSE);
430 
431  cust = dom_tree_to_customer (tree, book);
432  if (cust != NULL)
433  {
434  gdata->cb (tag, gdata->parsedata, cust);
435  }
436 
437  xmlFreeNode (tree);
438 
439  return cust != NULL;
440 }
441 
442 static sixtp*
443 customer_sixtp_parser_create (void)
444 {
445  return sixtp_dom_parser_new (gnc_customer_end_handler, NULL, NULL);
446 }
447 
448 static gboolean
449 customer_should_be_saved (GncCustomer* customer)
450 {
451  const char* id;
452 
453  /* make sure this is a valid customer before we save it -- should have an ID */
454  id = gncCustomerGetID (customer);
455  if (id == NULL || *id == '\0')
456  return FALSE;
457 
458  return TRUE;
459 }
460 
461 static void
462 do_count (QofInstance* cust_p, gpointer count_p)
463 {
464  int* count = static_cast<decltype (count)> (count_p);
465  if (customer_should_be_saved ((GncCustomer*)cust_p))
466  (*count)++;
467 }
468 
469 static int
470 customer_get_count (QofBook* book)
471 {
472  int count = 0;
473  qof_object_foreach (_GNC_MOD_NAME, book, do_count, (gpointer) &count);
474  return count;
475 }
476 
477 static void
478 xml_add_customer (QofInstance* cust_p, gpointer out_p)
479 {
480  xmlNodePtr node;
481  GncCustomer* cust = (GncCustomer*) cust_p;
482  FILE* out = static_cast<decltype (out)> (out_p);
483 
484  if (ferror (out))
485  return;
486  if (!customer_should_be_saved (cust))
487  return;
488 
489  node = customer_dom_tree_create (cust);
490  xmlElemDump (out, NULL, node);
491  xmlFreeNode (node);
492  if (ferror (out) || fprintf (out, "\n") < 0)
493  return;
494 }
495 
496 static gboolean
497 customer_write (FILE* out, QofBook* book)
498 {
499  qof_object_foreach_sorted (_GNC_MOD_NAME, book, xml_add_customer,
500  (gpointer) out);
501  return ferror (out) == 0;
502 }
503 
504 static gboolean
505 customer_ns (FILE* out)
506 {
507  g_return_val_if_fail (out, FALSE);
508  return gnc_xml2_write_namespace_decl (out, "cust");
509 }
510 
511 void
512 gnc_customer_xml_initialize (void)
513 {
514  static GncXmlDataType_t be_data =
515  {
516  GNC_FILE_BACKEND_VERS,
517  gnc_customer_string,
518  customer_sixtp_parser_create,
519  NULL, /* add_item */
520  customer_get_count,
521  customer_write,
522  NULL, /* scrub */
523  customer_ns,
524  };
525 
526  gnc_xml_register_backend (be_data);
527 }
Definition: sixtp.h:129
const GncGUID * qof_instance_get_guid(gconstpointer inst)
Return the GncGUID of this instance.
GncTaxIncluded
How to interpret the TaxIncluded.
Definition: gncTaxTable.h:85
#define PERR(format, args...)
Log a serious error.
Definition: qoflog.h:244
void qof_object_foreach_sorted(QofIdTypeConst type_name, QofBook *book, QofInstanceForeachCB cb, gpointer user_data)
Invoke callback &#39;cb&#39; on each instance in guid orted order.
Definition: qofobject.cpp:223
api for GnuCash version 2 XML-based file format
void qof_object_foreach(QofIdTypeConst type_name, QofBook *book, QofInstanceForeachCB cb, gpointer user_data)
Invoke the callback &#39;cb&#39; on every instance ov a particular object type.
Definition: qofobject.cpp:185
credit, discount and shipaddr are unique to GncCustomer id, name, notes, terms, addr, currency, taxtable, taxtable_override taxincluded, active and jobs are identical to ::GncVendor.
The type used to store guids in C.
Definition: guid.h:75
modtime is the internal date of the last modtime See libgnucash/engine/TaxTableBillTermImmutability.txt for an explanation of the following Code that handles refcount, parent, child, invisible and children is identical to that in ::GncBillTerm