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