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 
149 static gboolean
150 set_boolean (xmlNodePtr node, GncCustomer* cust,
151  void (*func) (GncCustomer* cust, gboolean b))
152 {
153  gint64 val;
154  gboolean ret;
155 
156  ret = dom_tree_to_integer (node, &val);
157  if (ret)
158  func (cust, (gboolean)val);
159 
160  return ret;
161 }
162 
163 static gboolean
164 customer_name_handler (xmlNodePtr node, gpointer cust_pdata)
165 {
166  struct customer_pdata* pdata = static_cast<decltype (pdata)> (cust_pdata);
167 
168  return apply_xmlnode_text (gncCustomerSetName, pdata->customer, node);
169 }
170 
171 static gboolean
172 customer_guid_handler (xmlNodePtr node, gpointer cust_pdata)
173 {
174  struct customer_pdata* pdata = static_cast<decltype (pdata)> (cust_pdata);
175  GncCustomer* cust;
176 
177  auto guid = dom_tree_to_guid (node);
178  g_return_val_if_fail (guid, FALSE);
179  cust = gncCustomerLookup (pdata->book, &*guid);
180  if (cust)
181  {
182  gncCustomerDestroy (pdata->customer);
183  pdata->customer = cust;
184  gncCustomerBeginEdit (cust);
185  }
186  else
187  {
188  gncCustomerSetGUID (pdata->customer, &*guid);
189  }
190 
191  return TRUE;
192 }
193 
194 static gboolean
195 customer_id_handler (xmlNodePtr node, gpointer cust_pdata)
196 {
197  struct customer_pdata* pdata = static_cast<decltype (pdata)> (cust_pdata);
198 
199  return apply_xmlnode_text (gncCustomerSetID, pdata->customer, node);
200 }
201 
202 static gboolean
203 customer_notes_handler (xmlNodePtr node, gpointer cust_pdata)
204 {
205  struct customer_pdata* pdata = static_cast<decltype (pdata)> (cust_pdata);
206 
207  return apply_xmlnode_text (gncCustomerSetNotes, pdata->customer, node);
208 }
209 
210 static gboolean
211 customer_terms_handler (xmlNodePtr node, gpointer cust_pdata)
212 {
213  struct customer_pdata* pdata = static_cast<decltype (pdata)> (cust_pdata);
214  GncBillTerm* term;
215 
216  auto guid = dom_tree_to_guid (node);
217  g_return_val_if_fail (guid, FALSE);
218  term = gnc_billterm_xml_find_or_create (pdata->book, &*guid);
219  g_assert (term);
220  gncCustomerSetTerms (pdata->customer, term);
221 
222  return TRUE;
223 }
224 
225 static gboolean
226 customer_addr_handler (xmlNodePtr node, gpointer cust_pdata)
227 {
228  struct customer_pdata* pdata = static_cast<decltype (pdata)> (cust_pdata);
229 
230  return gnc_dom_tree_to_address (node, gncCustomerGetAddr (pdata->customer));
231 }
232 
233 static gboolean
234 customer_shipaddr_handler (xmlNodePtr node, gpointer cust_pdata)
235 {
236  struct customer_pdata* pdata = static_cast<decltype (pdata)> (cust_pdata);
237 
238  return gnc_dom_tree_to_address (node,
239  gncCustomerGetShipAddr (pdata->customer));
240 }
241 
242 
243 static gboolean
244 customer_taxincluded_handler (xmlNodePtr node, gpointer cust_pdata)
245 {
246  struct customer_pdata* pdata = static_cast<decltype (pdata)> (cust_pdata);
247  auto set_tax_included = [](GncCustomer* cust, const char *str)
248  {
249  GncTaxIncluded type;
250  if (gncTaxIncludedStringToType (str, &type))
251  gncCustomerSetTaxIncluded (cust, type);
252  };
253  return apply_xmlnode_text (set_tax_included, pdata->customer, node);
254 }
255 
256 static gboolean
257 customer_active_handler (xmlNodePtr node, gpointer cust_pdata)
258 {
259  struct customer_pdata* pdata = static_cast<decltype (pdata)> (cust_pdata);
260  return set_boolean (node, pdata->customer, gncCustomerSetActive);
261 }
262 
263 static gboolean
264 customer_discount_handler (xmlNodePtr node, gpointer cust_pdata)
265 {
266  struct customer_pdata* pdata = static_cast<decltype (pdata)> (cust_pdata);
267 
268  gncCustomerSetDiscount (pdata->customer, dom_tree_to_gnc_numeric (node));
269  return TRUE;
270 }
271 
272 static gboolean
273 customer_credit_handler (xmlNodePtr node, gpointer cust_pdata)
274 {
275  struct customer_pdata* pdata = static_cast<decltype (pdata)> (cust_pdata);
276 
277  gncCustomerSetCredit (pdata->customer, dom_tree_to_gnc_numeric (node));
278  return TRUE;
279 }
280 
281 static gboolean
282 customer_currency_handler (xmlNodePtr node, gpointer customer_pdata)
283 {
284  struct customer_pdata* pdata = static_cast<decltype (pdata)> (customer_pdata);
285  gnc_commodity* com;
286 
287  com = dom_tree_to_commodity_ref (node, pdata->book);
288  g_return_val_if_fail (com, FALSE);
289 
290  gncCustomerSetCurrency (pdata->customer, com);
291 
292  return TRUE;
293 }
294 
295 static gboolean
296 customer_taxtable_handler (xmlNodePtr node, gpointer cust_pdata)
297 {
298  struct customer_pdata* pdata = static_cast<decltype (pdata)> (cust_pdata);
299  GncTaxTable* taxtable;
300 
301  auto guid = dom_tree_to_guid (node);
302  g_return_val_if_fail (guid, FALSE);
303  taxtable = gncTaxTableLookup (pdata->book, &*guid);
304  if (!taxtable)
305  {
306  taxtable = gncTaxTableCreate (pdata->book);
307  gncTaxTableBeginEdit (taxtable);
308  gncTaxTableSetGUID (taxtable, &*guid);
309  gncTaxTableCommitEdit (taxtable);
310  }
311  else
312  gncTaxTableDecRef (taxtable);
313 
314  gncCustomerSetTaxTable (pdata->customer, taxtable);
315  return TRUE;
316 }
317 
318 static gboolean
319 customer_taxtableoverride_handler (xmlNodePtr node, gpointer cust_pdata)
320 {
321  struct customer_pdata* pdata = static_cast<decltype (pdata)> (cust_pdata);
322  return set_boolean (node, pdata->customer, gncCustomerSetTaxTableOverride);
323 }
324 
325 static gboolean
326 customer_slots_handler (xmlNodePtr node, gpointer cust_pdata)
327 {
328  struct customer_pdata* pdata = static_cast<decltype (pdata)> (cust_pdata);
329  return dom_tree_create_instance_slots (node, QOF_INSTANCE (pdata->customer));
330 }
331 
332 static struct dom_tree_handler customer_handlers_v2[] =
333 {
334  { cust_name_string, customer_name_handler, 1, 0 },
335  { cust_guid_string, customer_guid_handler, 1, 0 },
336  { cust_id_string, customer_id_handler, 1, 0 },
337  { cust_addr_string, customer_addr_handler, 1, 0 },
338  { cust_shipaddr_string, customer_shipaddr_handler, 1, 0 },
339  { cust_notes_string, customer_notes_handler, 0, 0 },
340  { cust_terms_string, customer_terms_handler, 0, 0 },
341  { cust_taxincluded_string, customer_taxincluded_handler, 1, 0 },
342  { cust_active_string, customer_active_handler, 1, 0 },
343  { cust_discount_string, customer_discount_handler, 1, 0 },
344  { cust_credit_string, customer_credit_handler, 1, 0 },
345  { cust_currency_string, customer_currency_handler, 0, 0 }, /* XXX */
346  { "cust:commodity", customer_currency_handler, 0, 0 }, /* XXX */
347  { cust_taxtable_string, customer_taxtable_handler, 0, 0 },
348  { cust_taxtableoverride_string, customer_taxtableoverride_handler, 0, 0 },
349  { cust_slots_string, customer_slots_handler, 0, 0 },
350  { NULL, 0, 0, 0 }
351 };
352 
353 static GncCustomer*
354 dom_tree_to_customer (xmlNodePtr node, QofBook* book)
355 {
356  struct customer_pdata cust_pdata;
357  gboolean successful;
358 
359  cust_pdata.customer = gncCustomerCreate (book);
360  cust_pdata.book = book;
361  gncCustomerBeginEdit (cust_pdata.customer);
362 
363  successful = dom_tree_generic_parse (node, customer_handlers_v2,
364  &cust_pdata);
365 
366  if (successful)
367  gncCustomerCommitEdit (cust_pdata.customer);
368  else
369  {
370  PERR ("failed to parse customer tree");
371  gncCustomerDestroy (cust_pdata.customer);
372  cust_pdata.customer = NULL;
373  }
374 
375  return cust_pdata.customer;
376 }
377 
378 static gboolean
379 gnc_customer_end_handler (gpointer data_for_children,
380  GSList* data_from_children, GSList* sibling_data,
381  gpointer parent_data, gpointer global_data,
382  gpointer* result, const gchar* tag)
383 {
384  GncCustomer* cust;
385  xmlNodePtr tree = (xmlNodePtr)data_for_children;
386  gxpf_data* gdata = (gxpf_data*)global_data;
387  QofBook* book = static_cast<decltype (book)> (gdata->bookdata);
388 
389 
390  if (parent_data)
391  {
392  return TRUE;
393  }
394 
395  /* OK. For some messed up reason this is getting called again with a
396  NULL tag. So we ignore those cases */
397  if (!tag)
398  {
399  return TRUE;
400  }
401 
402  g_return_val_if_fail (tree, FALSE);
403 
404  cust = dom_tree_to_customer (tree, book);
405  if (cust != NULL)
406  {
407  gdata->cb (tag, gdata->parsedata, cust);
408  }
409 
410  xmlFreeNode (tree);
411 
412  return cust != NULL;
413 }
414 
415 static sixtp*
416 customer_sixtp_parser_create (void)
417 {
418  return sixtp_dom_parser_new (gnc_customer_end_handler, NULL, NULL);
419 }
420 
421 static gboolean
422 customer_should_be_saved (GncCustomer* customer)
423 {
424  const char* id;
425 
426  /* make sure this is a valid customer before we save it -- should have an ID */
427  id = gncCustomerGetID (customer);
428  if (id == NULL || *id == '\0')
429  return FALSE;
430 
431  return TRUE;
432 }
433 
434 static void
435 do_count (QofInstance* cust_p, gpointer count_p)
436 {
437  int* count = static_cast<decltype (count)> (count_p);
438  if (customer_should_be_saved ((GncCustomer*)cust_p))
439  (*count)++;
440 }
441 
442 static int
443 customer_get_count (QofBook* book)
444 {
445  int count = 0;
446  qof_object_foreach (_GNC_MOD_NAME, book, do_count, (gpointer) &count);
447  return count;
448 }
449 
450 static void
451 xml_add_customer (QofInstance* cust_p, gpointer out_p)
452 {
453  xmlNodePtr node;
454  GncCustomer* cust = (GncCustomer*) cust_p;
455  FILE* out = static_cast<decltype (out)> (out_p);
456 
457  if (ferror (out))
458  return;
459  if (!customer_should_be_saved (cust))
460  return;
461 
462  node = customer_dom_tree_create (cust);
463  xmlElemDump (out, NULL, node);
464  xmlFreeNode (node);
465  if (ferror (out) || fprintf (out, "\n") < 0)
466  return;
467 }
468 
469 static gboolean
470 customer_write (FILE* out, QofBook* book)
471 {
472  qof_object_foreach_sorted (_GNC_MOD_NAME, book, xml_add_customer,
473  (gpointer) out);
474  return ferror (out) == 0;
475 }
476 
477 static gboolean
478 customer_ns (FILE* out)
479 {
480  g_return_val_if_fail (out, FALSE);
481  return gnc_xml2_write_namespace_decl (out, "cust");
482 }
483 
484 void
485 gnc_customer_xml_initialize (void)
486 {
487  static GncXmlDataType_t be_data =
488  {
489  GNC_FILE_BACKEND_VERS,
490  gnc_customer_string,
491  customer_sixtp_parser_create,
492  NULL, /* add_item */
493  customer_get_count,
494  customer_write,
495  NULL, /* scrub */
496  customer_ns,
497  };
498 
499  gnc_xml_register_backend (be_data);
500 }
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.
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