Branch data Line data Source code
1 : : /********************************************************************\
2 : : * gnc-invoice-xml-v2.c -- invoice 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 "gncInvoiceP.h"
32 : :
33 : : #include "gnc-xml-helper.h"
34 : : #include "sixtp.h"
35 : : #include "sixtp-utils.h"
36 : : #include "sixtp-parsers.h"
37 : : #include "sixtp-utils.h"
38 : : #include "sixtp-dom-parsers.h"
39 : : #include "sixtp-dom-generators.h"
40 : : #include "gnc-invoice-xml-v2.h"
41 : : #include "gnc-owner-xml-v2.h"
42 : : #include "gnc-bill-term-xml-v2.h"
43 : :
44 : : #include "gnc-xml.h"
45 : : #include "io-gncxml-gen.h"
46 : : #include "io-gncxml-v2.h"
47 : :
48 : : #define _GNC_MOD_NAME GNC_ID_INVOICE
49 : :
50 : : static QofLogModule log_module = GNC_MOD_IO;
51 : :
52 : : const gchar* invoice_version_string = "2.0.0";
53 : :
54 : : /* ids */
55 : : #define gnc_invoice_string "gnc:GncInvoice"
56 : : #define invoice_guid_string "invoice:guid"
57 : : #define invoice_id_string "invoice:id"
58 : : #define invoice_owner_string "invoice:owner"
59 : : #define invoice_opened_string "invoice:opened"
60 : : #define invoice_posted_string "invoice:posted"
61 : : #define invoice_terms_string "invoice:terms"
62 : : #define invoice_billing_id_string "invoice:billing_id"
63 : : #define invoice_notes_string "invoice:notes"
64 : : #define invoice_active_string "invoice:active"
65 : : #define invoice_posttxn_string "invoice:posttxn"
66 : : #define invoice_postlot_string "invoice:postlot"
67 : : #define invoice_postacc_string "invoice:postacc"
68 : : #define invoice_currency_string "invoice:currency"
69 : : #define invoice_billto_string "invoice:billto"
70 : : #define invoice_tochargeamt_string "invoice:charge-amt"
71 : : #define invoice_slots_string "invoice:slots"
72 : :
73 : : static void
74 : 4 : maybe_add_string (xmlNodePtr ptr, const char* tag, const char* str)
75 : : {
76 : 4 : if (str && *str)
77 : 2 : xmlAddChild (ptr, text_to_dom_tree (tag, str));
78 : 4 : }
79 : :
80 : : static void
81 : 2 : maybe_add_time64 (xmlNodePtr ptr, const char* tag, time64 time)
82 : : {
83 : 2 : if (time != INT64_MAX)
84 : 2 : xmlAddChild (ptr, time64_to_dom_tree (tag, time));
85 : 2 : }
86 : :
87 : : static xmlNodePtr
88 : 2 : invoice_dom_tree_create (GncInvoice* invoice)
89 : : {
90 : : xmlNodePtr ret;
91 : : time64 time;
92 : : Transaction* txn;
93 : : GNCLot* lot;
94 : : Account* acc;
95 : : GncBillTerm* term;
96 : : GncOwner* billto;
97 : : gnc_numeric amt;
98 : :
99 : 2 : ret = xmlNewNode (NULL, BAD_CAST gnc_invoice_string);
100 : 2 : xmlSetProp (ret, BAD_CAST "version", BAD_CAST invoice_version_string);
101 : :
102 : 2 : xmlAddChild (ret, guid_to_dom_tree (invoice_guid_string,
103 : 2 : qof_instance_get_guid (QOF_INSTANCE (invoice))));
104 : :
105 : 2 : xmlAddChild (ret, text_to_dom_tree (invoice_id_string,
106 : : gncInvoiceGetID (invoice)));
107 : :
108 : 2 : xmlAddChild (ret, gnc_owner_to_dom_tree (invoice_owner_string,
109 : : gncInvoiceGetOwner (invoice)));
110 : :
111 : 2 : time = gncInvoiceGetDateOpened (invoice);
112 : 2 : xmlAddChild (ret, time64_to_dom_tree (invoice_opened_string, time));
113 : :
114 : 2 : maybe_add_time64 (ret, invoice_posted_string, gncInvoiceGetDatePosted (invoice));
115 : :
116 : 2 : term = gncInvoiceGetTerms (invoice);
117 : 2 : if (term)
118 : 0 : xmlAddChild (ret, guid_to_dom_tree (invoice_terms_string,
119 : 0 : qof_instance_get_guid (QOF_INSTANCE (term))));
120 : :
121 : 2 : maybe_add_string (ret, invoice_billing_id_string,
122 : : gncInvoiceGetBillingID (invoice));
123 : 2 : maybe_add_string (ret, invoice_notes_string, gncInvoiceGetNotes (invoice));
124 : :
125 : 2 : xmlAddChild (ret, int_to_dom_tree (invoice_active_string,
126 : 2 : gncInvoiceGetActive (invoice)));
127 : :
128 : 2 : txn = gncInvoiceGetPostedTxn (invoice);
129 : 2 : if (txn)
130 : 2 : xmlAddChild (ret, guid_to_dom_tree (invoice_posttxn_string,
131 : 2 : xaccTransGetGUID (txn)));
132 : :
133 : 2 : lot = gncInvoiceGetPostedLot (invoice);
134 : 2 : if (lot)
135 : 2 : xmlAddChild (ret, guid_to_dom_tree (invoice_postlot_string,
136 : 2 : gnc_lot_get_guid (lot)));
137 : :
138 : 2 : acc = gncInvoiceGetPostedAcc (invoice);
139 : 2 : if (acc)
140 : 2 : xmlAddChild (ret, guid_to_dom_tree (invoice_postacc_string,
141 : 2 : qof_instance_get_guid (QOF_INSTANCE (acc))));
142 : :
143 : : xmlAddChild
144 : 2 : (ret,
145 : : commodity_ref_to_dom_tree (invoice_currency_string,
146 : 2 : gncInvoiceGetCurrency (invoice)));
147 : :
148 : 2 : billto = gncInvoiceGetBillTo (invoice);
149 : 2 : if (billto && billto->owner.undefined != NULL)
150 : 0 : xmlAddChild (ret, gnc_owner_to_dom_tree (invoice_billto_string, billto));
151 : :
152 : 2 : amt = gncInvoiceGetToChargeAmount (invoice);
153 : 2 : if (! gnc_numeric_zero_p (amt))
154 : 0 : xmlAddChild (ret, gnc_numeric_to_dom_tree (invoice_tochargeamt_string, &amt));
155 : :
156 : : /* xmlAddChild won't do anything with a NULL, so tests are superfluous. */
157 : 2 : xmlAddChild (ret, qof_instance_slots_to_dom_tree (invoice_slots_string,
158 : 2 : QOF_INSTANCE (invoice)));
159 : 2 : return ret;
160 : : }
161 : :
162 : : /***********************************************************************/
163 : :
164 : : struct invoice_pdata
165 : : {
166 : : GncInvoice* invoice;
167 : : QofBook* book;
168 : : };
169 : :
170 : :
171 : : static inline gboolean
172 : 7 : set_time64 (xmlNodePtr node, GncInvoice* invoice,
173 : : void (*func) (GncInvoice* invoice, time64 time))
174 : : {
175 : 7 : time64 time = dom_tree_to_time64 (node);
176 : 7 : if (!dom_tree_valid_time64 (time, node->name)) time = 0;
177 : 7 : func (invoice, time);
178 : 7 : return TRUE;
179 : : }
180 : :
181 : : static gboolean
182 : 4 : invoice_guid_handler (xmlNodePtr node, gpointer invoice_pdata)
183 : : {
184 : 4 : struct invoice_pdata* pdata = static_cast<decltype (pdata)> (invoice_pdata);
185 : : GncInvoice* invoice;
186 : :
187 : 4 : auto guid = dom_tree_to_guid (node);
188 : 4 : g_return_val_if_fail (guid, FALSE);
189 : 4 : invoice = gncInvoiceLookup (pdata->book, &*guid);
190 : 4 : if (invoice)
191 : : {
192 : 4 : gncInvoiceDestroy (pdata->invoice);
193 : 4 : pdata->invoice = invoice;
194 : 4 : gncInvoiceBeginEdit (invoice);
195 : : }
196 : : else
197 : : {
198 : 0 : gncInvoiceSetGUID (pdata->invoice, &*guid);
199 : : }
200 : :
201 : 4 : return TRUE;
202 : : }
203 : :
204 : : static gboolean
205 : 4 : invoice_id_handler (xmlNodePtr node, gpointer invoice_pdata)
206 : : {
207 : 4 : struct invoice_pdata* pdata = static_cast<decltype (pdata)> (invoice_pdata);
208 : :
209 : 4 : return apply_xmlnode_text (gncInvoiceSetID, pdata->invoice, node);
210 : : }
211 : :
212 : : static gboolean
213 : 4 : invoice_owner_handler (xmlNodePtr node, gpointer invoice_pdata)
214 : : {
215 : 4 : struct invoice_pdata* pdata = static_cast<decltype (pdata)> (invoice_pdata);
216 : : GncOwner owner;
217 : : gboolean ret;
218 : :
219 : 4 : ret = gnc_dom_tree_to_owner (node, &owner, pdata->book);
220 : 4 : if (ret)
221 : 4 : gncInvoiceSetOwner (pdata->invoice, &owner);
222 : :
223 : 4 : return ret;
224 : : }
225 : :
226 : : static gboolean
227 : 4 : invoice_opened_handler (xmlNodePtr node, gpointer invoice_pdata)
228 : : {
229 : 4 : struct invoice_pdata* pdata = static_cast<decltype (pdata)> (invoice_pdata);
230 : 4 : return set_time64 (node, pdata->invoice, gncInvoiceSetDateOpened);
231 : : }
232 : :
233 : : static gboolean
234 : 3 : invoice_posted_handler (xmlNodePtr node, gpointer invoice_pdata)
235 : : {
236 : 3 : struct invoice_pdata* pdata = static_cast<decltype (pdata)> (invoice_pdata);
237 : 3 : return set_time64 (node, pdata->invoice, gncInvoiceSetDatePosted);
238 : : }
239 : :
240 : : static gboolean
241 : 2 : invoice_billing_id_handler (xmlNodePtr node, gpointer invoice_pdata)
242 : : {
243 : 2 : struct invoice_pdata* pdata = static_cast<decltype (pdata)> (invoice_pdata);
244 : :
245 : 2 : return apply_xmlnode_text (gncInvoiceSetBillingID, pdata->invoice, node);
246 : : }
247 : :
248 : : static gboolean
249 : 0 : invoice_notes_handler (xmlNodePtr node, gpointer invoice_pdata)
250 : : {
251 : 0 : struct invoice_pdata* pdata = static_cast<decltype (pdata)> (invoice_pdata);
252 : :
253 : 0 : return apply_xmlnode_text (gncInvoiceSetNotes, pdata->invoice, node);
254 : : }
255 : :
256 : : static gboolean
257 : 4 : invoice_active_handler (xmlNodePtr node, gpointer invoice_pdata)
258 : : {
259 : 4 : struct invoice_pdata* pdata = static_cast<decltype (pdata)> (invoice_pdata);
260 : : gint64 val;
261 : : gboolean ret;
262 : :
263 : 4 : ret = dom_tree_to_integer (node, &val);
264 : 4 : if (ret)
265 : 4 : gncInvoiceSetActive (pdata->invoice, (gboolean)val);
266 : :
267 : 4 : return ret;
268 : : }
269 : :
270 : : static gboolean
271 : 0 : invoice_terms_handler (xmlNodePtr node, gpointer invoice_pdata)
272 : : {
273 : 0 : struct invoice_pdata* pdata = static_cast<decltype (pdata)> (invoice_pdata);
274 : : GncBillTerm* term;
275 : :
276 : 0 : auto guid = dom_tree_to_guid (node);
277 : 0 : g_return_val_if_fail (guid, FALSE);
278 : 0 : term = gnc_billterm_xml_find_or_create (pdata->book, &*guid);
279 : 0 : g_assert (term);
280 : 0 : gncInvoiceSetTerms (pdata->invoice, term);
281 : :
282 : 0 : return TRUE;
283 : : }
284 : :
285 : : static gboolean
286 : 3 : invoice_posttxn_handler (xmlNodePtr node, gpointer invoice_pdata)
287 : : {
288 : 3 : struct invoice_pdata* pdata = static_cast<decltype (pdata)> (invoice_pdata);
289 : : Transaction* txn;
290 : :
291 : 3 : auto guid = dom_tree_to_guid (node);
292 : 3 : g_return_val_if_fail (guid, FALSE);
293 : 3 : txn = xaccTransLookup (&*guid, pdata->book);
294 : 3 : g_return_val_if_fail (txn, FALSE);
295 : :
296 : 3 : gncInvoiceSetPostedTxn (pdata->invoice, txn);
297 : 3 : return TRUE;
298 : : }
299 : :
300 : : static gboolean
301 : 3 : invoice_postlot_handler (xmlNodePtr node, gpointer invoice_pdata)
302 : : {
303 : 3 : struct invoice_pdata* pdata = static_cast<decltype (pdata)> (invoice_pdata);
304 : : GNCLot* lot;
305 : :
306 : 3 : auto guid = dom_tree_to_guid (node);
307 : 3 : g_return_val_if_fail (guid, FALSE);
308 : 3 : lot = gnc_lot_lookup (&*guid, pdata->book);
309 : 3 : g_return_val_if_fail (lot, FALSE);
310 : :
311 : 3 : gncInvoiceSetPostedLot (pdata->invoice, lot);
312 : 3 : return TRUE;
313 : : }
314 : :
315 : : static gboolean
316 : 3 : invoice_postacc_handler (xmlNodePtr node, gpointer invoice_pdata)
317 : : {
318 : 3 : struct invoice_pdata* pdata = static_cast<decltype (pdata)> (invoice_pdata);
319 : : Account* acc;
320 : :
321 : 3 : auto guid = dom_tree_to_guid (node);
322 : 3 : g_return_val_if_fail (guid, FALSE);
323 : 3 : acc = xaccAccountLookup (&*guid, pdata->book);
324 : 3 : g_return_val_if_fail (acc, FALSE);
325 : :
326 : 3 : gncInvoiceSetPostedAcc (pdata->invoice, acc);
327 : 3 : return TRUE;
328 : : }
329 : :
330 : : static gboolean
331 : 4 : invoice_currency_handler (xmlNodePtr node, gpointer invoice_pdata)
332 : : {
333 : 4 : struct invoice_pdata* pdata = static_cast<decltype (pdata)> (invoice_pdata);
334 : : gnc_commodity* com;
335 : :
336 : 4 : com = dom_tree_to_commodity_ref (node, pdata->book);
337 : 4 : g_return_val_if_fail (com, FALSE);
338 : :
339 : 4 : gncInvoiceSetCurrency (pdata->invoice, com);
340 : :
341 : 4 : return TRUE;
342 : : }
343 : :
344 : : static gboolean
345 : 0 : invoice_billto_handler (xmlNodePtr node, gpointer invoice_pdata)
346 : : {
347 : 0 : struct invoice_pdata* pdata = static_cast<decltype (pdata)> (invoice_pdata);
348 : : GncOwner owner;
349 : : gboolean ret;
350 : :
351 : 0 : ret = gnc_dom_tree_to_owner (node, &owner, pdata->book);
352 : 0 : if (ret)
353 : 0 : gncInvoiceSetBillTo (pdata->invoice, &owner);
354 : :
355 : 0 : return ret;
356 : : }
357 : :
358 : : static gboolean
359 : 0 : invoice_tochargeamt_handler (xmlNodePtr node, gpointer invoice_pdata)
360 : : {
361 : 0 : struct invoice_pdata* pdata = static_cast<decltype (pdata)> (invoice_pdata);
362 : :
363 : 0 : gncInvoiceSetToChargeAmount (pdata->invoice, dom_tree_to_gnc_numeric (node));
364 : 0 : return TRUE;
365 : : }
366 : :
367 : : static gboolean
368 : 3 : invoice_slots_handler (xmlNodePtr node, gpointer invoice_pdata)
369 : : {
370 : 3 : struct invoice_pdata* pdata = static_cast<decltype (pdata)> (invoice_pdata);
371 : 3 : return dom_tree_create_instance_slots (node, QOF_INSTANCE (pdata->invoice));
372 : : }
373 : :
374 : : static struct dom_tree_handler invoice_handlers_v2[] =
375 : : {
376 : : { invoice_guid_string, invoice_guid_handler, 1, 0 },
377 : : { invoice_id_string, invoice_id_handler, 1, 0 },
378 : : { invoice_owner_string, invoice_owner_handler, 1, 0 },
379 : : { invoice_opened_string, invoice_opened_handler, 1, 0 },
380 : : { invoice_posted_string, invoice_posted_handler, 0, 0 },
381 : : { invoice_billing_id_string, invoice_billing_id_handler, 0, 0 },
382 : : { invoice_notes_string, invoice_notes_handler, 0, 0 },
383 : : { invoice_active_string, invoice_active_handler, 1, 0 },
384 : : { invoice_terms_string, invoice_terms_handler, 0, 0 },
385 : : { invoice_posttxn_string, invoice_posttxn_handler, 0, 0 },
386 : : { invoice_postlot_string, invoice_postlot_handler, 0, 0 },
387 : : { invoice_postacc_string, invoice_postacc_handler, 0, 0 },
388 : : { invoice_currency_string, invoice_currency_handler, 0, 0 },
389 : : { "invoice:commodity", invoice_currency_handler, 0, 0 },
390 : : { invoice_billto_string, invoice_billto_handler, 0, 0 },
391 : : { invoice_tochargeamt_string, invoice_tochargeamt_handler, 0, 0},
392 : : { invoice_slots_string, invoice_slots_handler, 0, 0 },
393 : : { NULL, 0, 0, 0 }
394 : : };
395 : :
396 : : static GncInvoice*
397 : 4 : dom_tree_to_invoice (xmlNodePtr node, QofBook* book)
398 : : {
399 : : struct invoice_pdata invoice_pdata;
400 : : gboolean successful;
401 : :
402 : 4 : invoice_pdata.invoice = gncInvoiceCreate (book);
403 : 4 : invoice_pdata.book = book;
404 : 4 : gncInvoiceBeginEdit (invoice_pdata.invoice);
405 : :
406 : 4 : successful = dom_tree_generic_parse (node, invoice_handlers_v2,
407 : : &invoice_pdata);
408 : :
409 : 4 : if (successful)
410 : 4 : gncInvoiceCommitEdit (invoice_pdata.invoice);
411 : : else
412 : : {
413 : 0 : PERR ("failed to parse invoice tree");
414 : 0 : gncInvoiceDestroy (invoice_pdata.invoice);
415 : 0 : invoice_pdata.invoice = NULL;
416 : : }
417 : :
418 : 4 : return invoice_pdata.invoice;
419 : : }
420 : :
421 : : static gboolean
422 : 77 : gnc_invoice_end_handler (gpointer data_for_children,
423 : : GSList* data_from_children, GSList* sibling_data,
424 : : gpointer parent_data, gpointer global_data,
425 : : gpointer* result, const gchar* tag)
426 : : {
427 : : GncInvoice* invoice;
428 : 77 : xmlNodePtr tree = (xmlNodePtr)data_for_children;
429 : 77 : gxpf_data* gdata = (gxpf_data*)global_data;
430 : 77 : QofBook* book = static_cast<decltype (book)> (gdata->bookdata);
431 : :
432 : 77 : if (parent_data)
433 : : {
434 : 73 : 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 : 4 : if (!tag)
440 : : {
441 : 0 : return TRUE;
442 : : }
443 : :
444 : 4 : g_return_val_if_fail (tree, FALSE);
445 : :
446 : 4 : invoice = dom_tree_to_invoice (tree, book);
447 : 4 : if (invoice != NULL)
448 : : {
449 : 4 : gdata->cb (tag, gdata->parsedata, invoice);
450 : : }
451 : :
452 : 4 : xmlFreeNode (tree);
453 : :
454 : 4 : return invoice != NULL;
455 : : }
456 : :
457 : : static sixtp*
458 : 22 : invoice_sixtp_parser_create (void)
459 : : {
460 : 22 : return sixtp_dom_parser_new (gnc_invoice_end_handler, NULL, NULL);
461 : : }
462 : :
463 : : static gboolean
464 : 4 : invoice_should_be_saved (GncInvoice* invoice)
465 : : {
466 : : const char* id;
467 : :
468 : : /* make sure this is a valid invoice before we save it -- should have an ID */
469 : 4 : id = gncInvoiceGetID (invoice);
470 : 4 : if (id == NULL || *id == '\0')
471 : 0 : return FALSE;
472 : :
473 : 4 : return TRUE;
474 : : }
475 : :
476 : : static void
477 : 2 : do_count (QofInstance* invoice_p, gpointer count_p)
478 : : {
479 : 2 : int* count = static_cast<decltype (count)> (count_p);
480 : 2 : if (invoice_should_be_saved ((GncInvoice*)invoice_p))
481 : 2 : (*count)++;
482 : 2 : }
483 : :
484 : : static int
485 : 4 : invoice_get_count (QofBook* book)
486 : : {
487 : 4 : int count = 0;
488 : 4 : qof_object_foreach (_GNC_MOD_NAME, book, do_count, (gpointer) &count);
489 : 4 : return count;
490 : : }
491 : :
492 : : static void
493 : 2 : xml_add_invoice (QofInstance* invoice_p, gpointer out_p)
494 : : {
495 : : xmlNodePtr node;
496 : 2 : GncInvoice* invoice = (GncInvoice*) invoice_p;
497 : 2 : FILE* out = static_cast<decltype (out)> (out_p);
498 : :
499 : 2 : if (ferror (out))
500 : 0 : return;
501 : 2 : if (!invoice_should_be_saved (invoice))
502 : 0 : return;
503 : :
504 : 2 : node = invoice_dom_tree_create (invoice);
505 : 2 : xmlElemDump (out, NULL, node);
506 : 2 : xmlFreeNode (node);
507 : 2 : if (ferror (out) || fprintf (out, "\n") < 0)
508 : 0 : return;
509 : : }
510 : :
511 : : static gboolean
512 : 4 : invoice_write (FILE* out, QofBook* book)
513 : : {
514 : 4 : qof_object_foreach_sorted (_GNC_MOD_NAME, book, xml_add_invoice,
515 : : (gpointer) out);
516 : 4 : return ferror (out) == 0;
517 : : }
518 : :
519 : : static gboolean
520 : 4 : invoice_ns (FILE* out)
521 : : {
522 : 4 : g_return_val_if_fail (out, FALSE);
523 : 4 : return gnc_xml2_write_namespace_decl (out, "invoice");
524 : : }
525 : :
526 : : void
527 : 15 : gnc_invoice_xml_initialize (void)
528 : : {
529 : : static GncXmlDataType_t be_data =
530 : : {
531 : : GNC_FILE_BACKEND_VERS,
532 : : gnc_invoice_string,
533 : : invoice_sixtp_parser_create,
534 : : NULL, /* add_item */
535 : : invoice_get_count,
536 : : invoice_write,
537 : : NULL, /* scrub */
538 : : invoice_ns,
539 : : };
540 : :
541 : 15 : gnc_xml_register_backend(be_data);
542 : 15 : }
|