1 /********************************************************************\
2  * gncTaxTable.c -- the Gnucash Tax Table interface                 *
3  *                                                                  *
4  * This program is free software; you can redistribute it and/or    *
5  * modify it under the terms of the GNU General Public License as   *
6  * published by the Free Software Foundation; either version 2 of   *
7  * the License, or (at your option) any later version.              *
8  *                                                                  *
9  * This program is distributed in the hope that it will be useful,  *
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
12  * GNU General Public License for more details.                     *
13  *                                                                  *
14  * You should have received a copy of the GNU General Public License*
15  * along with this program; if not, contact:                        *
16  *                                                                  *
17  * Free Software Foundation           Voice:  +1-617-542-5942       *
18  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
19  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
20  *                                                                  *
21 \********************************************************************/
22 
23 /*
24  * Copyright (C) 2002 Derek Atkins
25  * Copyright (C) 2003 Linas Vepstas <linas@linas.org>
26  * Author: Derek Atkins <warlord@MIT.EDU>
27  */
28 
29 #include <config.h>
30 
31 #include <glib.h>
32 #include <qofinstance-p.h>
33 
34 #include "gnc-features.h"
35 #include "gncTaxTableP.h"
36 
37 struct _gncTaxTable
38 {
39     QofInstance     inst;
40     const char *    name;
41     GncTaxTableEntryList*  entries;
42     time64          modtime;      /* internal date of last modtime */
43 
44     /* See src/doc/business.txt for an explanation of the following */
45     /* Code that handles this is *identical* to that in gncBillTerm */
46     gint64          refcount;
47     GncTaxTable *   parent;       /* if non-null, we are an immutable child */
48     GncTaxTable *   child;        /* if non-null, we have not changed */
49     gboolean        invisible;
50     GList *         children;     /* list of children for disconnection */
51 };
52 
53 struct _gncTaxTableClass
54 {
55     QofInstanceClass parent_class;
56 };
57 
58 struct _gncTaxTableEntry
59 {
60     GncTaxTable *   table;
61     Account *       account;
62     GncAmountType   type;
63     gnc_numeric     amount;
64 };
65 
66 struct _book_info
67 {
68     GList *         tables;          /* visible tables */
69 };
70 
71 static QofLogModule log_module = GNC_MOD_BUSINESS;
72 
73 /* =============================================================== */
74 /* You must edit the functions in this block in tandem.  KEEP THEM IN
75    SYNC! */
76 
77 #define GNC_RETURN_ENUM_AS_STRING(x,s) case (x): return (s);
78 const char *
gncAmountTypeToString(GncAmountType type)79 gncAmountTypeToString (GncAmountType type)
80 {
81     switch (type)
82     {
83         GNC_RETURN_ENUM_AS_STRING(GNC_AMT_TYPE_VALUE, "VALUE");
84         GNC_RETURN_ENUM_AS_STRING(GNC_AMT_TYPE_PERCENT, "PERCENT");
85     default:
86         g_warning ("asked to translate unknown amount type %d.\n", type);
87         break;
88     }
89     return(NULL);
90 }
91 
92 const char *
gncTaxIncludedTypeToString(GncTaxIncluded type)93 gncTaxIncludedTypeToString (GncTaxIncluded type)
94 {
95     switch (type)
96     {
97         GNC_RETURN_ENUM_AS_STRING(GNC_TAXINCLUDED_YES, "YES");
98         GNC_RETURN_ENUM_AS_STRING(GNC_TAXINCLUDED_NO, "NO");
99         GNC_RETURN_ENUM_AS_STRING(GNC_TAXINCLUDED_USEGLOBAL, "USEGLOBAL");
100     default:
101         g_warning ("asked to translate unknown taxincluded type %d.\n", type);
102         break;
103     }
104     return(NULL);
105 }
106 #undef GNC_RETURN_ENUM_AS_STRING
107 #define GNC_RETURN_ON_MATCH(s,x) \
108   if(g_strcmp0((s), (str)) == 0) { *type = x; return(TRUE); }
109 gboolean
gncAmountStringToType(const char * str,GncAmountType * type)110 gncAmountStringToType (const char *str, GncAmountType *type)
111 {
112     GNC_RETURN_ON_MATCH ("VALUE", GNC_AMT_TYPE_VALUE);
113     GNC_RETURN_ON_MATCH ("PERCENT", GNC_AMT_TYPE_PERCENT);
114     g_warning ("asked to translate unknown amount type string %s.\n",
115                str ? str : "(null)");
116 
117     return(FALSE);
118 }
119 
120 gboolean
gncTaxIncludedStringToType(const char * str,GncTaxIncluded * type)121 gncTaxIncludedStringToType (const char *str, GncTaxIncluded *type)
122 {
123     GNC_RETURN_ON_MATCH ("YES", GNC_TAXINCLUDED_YES);
124     GNC_RETURN_ON_MATCH ("NO", GNC_TAXINCLUDED_NO);
125     GNC_RETURN_ON_MATCH ("USEGLOBAL", GNC_TAXINCLUDED_USEGLOBAL);
126     g_warning ("asked to translate unknown taxincluded type string %s.\n",
127                str ? str : "(null)");
128 
129     return(FALSE);
130 }
131 #undef GNC_RETURN_ON_MATCH
132 
133 /* =============================================================== */
134 /* Misc inline functions */
135 
136 #define _GNC_MOD_NAME        GNC_ID_TAXTABLE
137 
138 #define SET_STR(obj, member, str) { \
139         if (!g_strcmp0 (member, str)) return; \
140         gncTaxTableBeginEdit (obj); \
141         CACHE_REPLACE (member, str); \
142         }
143 
144 static inline void
mark_table(GncTaxTable * table)145 mark_table (GncTaxTable *table)
146 {
147     qof_instance_set_dirty(&table->inst);
148     qof_event_gen (&table->inst, QOF_EVENT_MODIFY, NULL);
149 }
150 
151 static inline void
maybe_resort_list(GncTaxTable * table)152 maybe_resort_list (GncTaxTable *table)
153 {
154     struct _book_info *bi;
155 
156     if (table->parent || table->invisible) return;
157     bi = qof_book_get_data (qof_instance_get_book(table), _GNC_MOD_NAME);
158     bi->tables = g_list_sort (bi->tables, (GCompareFunc)gncTaxTableCompare);
159 }
160 
161 static inline void
mod_table(GncTaxTable * table)162 mod_table (GncTaxTable *table)
163 {
164     table->modtime = gnc_time (NULL);
165 }
166 
addObj(GncTaxTable * table)167 static inline void addObj (GncTaxTable *table)
168 {
169     struct _book_info *bi;
170     bi = qof_book_get_data (qof_instance_get_book(table), _GNC_MOD_NAME);
171     bi->tables = g_list_insert_sorted (bi->tables, table,
172                                        (GCompareFunc)gncTaxTableCompare);
173 }
174 
remObj(GncTaxTable * table)175 static inline void remObj (GncTaxTable *table)
176 {
177     struct _book_info *bi;
178     bi = qof_book_get_data (qof_instance_get_book(table), _GNC_MOD_NAME);
179     bi->tables = g_list_remove (bi->tables, table);
180 }
181 
182 static inline void
gncTaxTableAddChild(GncTaxTable * table,GncTaxTable * child)183 gncTaxTableAddChild (GncTaxTable *table, GncTaxTable *child)
184 {
185     g_return_if_fail(table);
186     g_return_if_fail(child);
187     g_return_if_fail(qof_instance_get_destroying(table) == FALSE);
188 
189     table->children = g_list_prepend(table->children, child);
190 }
191 
192 static inline void
gncTaxTableRemoveChild(GncTaxTable * table,const GncTaxTable * child)193 gncTaxTableRemoveChild (GncTaxTable *table, const GncTaxTable *child)
194 {
195     g_return_if_fail(table);
196     g_return_if_fail(child);
197 
198     if (qof_instance_get_destroying(table)) return;
199 
200     table->children = g_list_remove(table->children, child);
201 }
202 
203 /* =============================================================== */
204 
205 enum
206 {
207     PROP_0,
208     PROP_NAME,		/* Table */
209     PROP_INVISIBLE,	/* Table */
210     PROP_REFCOUNT,	/* Table */
211 //  PROP_PARENT,	/* Table */
212 };
213 
214 /* GObject Initialization */
215 G_DEFINE_TYPE(GncTaxTable, gnc_taxtable, QOF_TYPE_INSTANCE);
216 
217 static void
gnc_taxtable_init(GncTaxTable * tt)218 gnc_taxtable_init(GncTaxTable* tt)
219 {
220 }
221 
222 static void
gnc_taxtable_dispose(GObject * ttp)223 gnc_taxtable_dispose(GObject *ttp)
224 {
225     G_OBJECT_CLASS(gnc_taxtable_parent_class)->dispose(ttp);
226 }
227 
228 static void
gnc_taxtable_finalize(GObject * ttp)229 gnc_taxtable_finalize(GObject* ttp)
230 {
231     G_OBJECT_CLASS(gnc_taxtable_parent_class)->dispose(ttp);
232 }
233 
234 static void
gnc_taxtable_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)235 gnc_taxtable_get_property (GObject         *object,
236                            guint            prop_id,
237                            GValue          *value,
238                            GParamSpec      *pspec)
239 {
240     GncTaxTable *tt;
241 
242     g_return_if_fail(GNC_IS_TAXTABLE(object));
243 
244     tt = GNC_TAXTABLE(object);
245     switch (prop_id)
246     {
247     case PROP_NAME:
248         g_value_set_string(value, tt->name);
249         break;
250     case PROP_INVISIBLE:
251         g_value_set_boolean(value, tt->invisible);
252         break;
253     case PROP_REFCOUNT:
254         g_value_set_uint64(value, tt->refcount);
255         break;
256     default:
257         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
258         break;
259     }
260 }
261 
262 static void
gnc_taxtable_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)263 gnc_taxtable_set_property (GObject         *object,
264                            guint            prop_id,
265                            const GValue          *value,
266                            GParamSpec      *pspec)
267 {
268     GncTaxTable *tt;
269 
270     g_return_if_fail(GNC_IS_TAXTABLE(object));
271 
272     tt = GNC_TAXTABLE(object);
273     g_assert (qof_instance_get_editlevel(tt));
274 
275     switch (prop_id)
276     {
277     case PROP_NAME:
278         gncTaxTableSetName(tt, g_value_get_string(value));
279         break;
280     case PROP_INVISIBLE:
281         if (g_value_get_boolean(value))
282         {
283             gncTaxTableMakeInvisible(tt);
284         }
285         break;
286     case PROP_REFCOUNT:
287         gncTaxTableSetRefcount(tt, g_value_get_uint64(value));
288         break;
289     default:
290         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
291         break;
292     }
293 }
294 
295 /** Return displayable name */
296 static gchar*
impl_get_display_name(const QofInstance * inst)297 impl_get_display_name(const QofInstance* inst)
298 {
299     GncTaxTable* tt;
300 
301     g_return_val_if_fail(inst != NULL, FALSE);
302     g_return_val_if_fail(GNC_IS_TAXTABLE(inst), FALSE);
303 
304     tt = GNC_TAXTABLE(inst);
305     return g_strdup_printf("Tax table %s", tt->name);
306 }
307 
308 /** Does this object refer to a specific object */
309 static gboolean
impl_refers_to_object(const QofInstance * inst,const QofInstance * ref)310 impl_refers_to_object(const QofInstance* inst, const QofInstance* ref)
311 {
312     GncTaxTable* tt;
313 
314     g_return_val_if_fail(inst != NULL, FALSE);
315     g_return_val_if_fail(GNC_IS_TAXTABLE(inst), FALSE);
316 
317     tt = GNC_TAXTABLE(inst);
318 
319     if (GNC_IS_ACCOUNT(ref))
320     {
321         GList* node;
322 
323         for (node = tt->entries; node != NULL; node = node->next)
324         {
325             GncTaxTableEntry* tte = node->data;
326 
327             if (tte->account == GNC_ACCOUNT(ref))
328             {
329                 return TRUE;
330             }
331         }
332     }
333 
334     return FALSE;
335 }
336 
337 /** Returns a list of my type of object which refers to an object.  For example, when called as
338         qof_instance_get_typed_referring_object_list(taxtable, account);
339     it will return the list of taxtables which refer to a specific account.  The result should be the
340     same regardless of which taxtable object is used.  The list must be freed by the caller but the
341     objects on the list must not.
342  */
343 static GList*
impl_get_typed_referring_object_list(const QofInstance * inst,const QofInstance * ref)344 impl_get_typed_referring_object_list(const QofInstance* inst, const QofInstance* ref)
345 {
346     if (!GNC_IS_ACCOUNT(ref))
347     {
348         return NULL;
349     }
350 
351     return qof_instance_get_referring_object_list_from_collection(qof_instance_get_collection(inst), ref);
352 }
353 
354 static void
gnc_taxtable_class_init(GncTaxTableClass * klass)355 gnc_taxtable_class_init (GncTaxTableClass *klass)
356 {
357     GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
358     QofInstanceClass* qof_class = QOF_INSTANCE_CLASS(klass);
359 
360     gobject_class->dispose = gnc_taxtable_dispose;
361     gobject_class->finalize = gnc_taxtable_finalize;
362     gobject_class->set_property = gnc_taxtable_set_property;
363     gobject_class->get_property = gnc_taxtable_get_property;
364 
365     qof_class->get_display_name = impl_get_display_name;
366     qof_class->refers_to_object = impl_refers_to_object;
367     qof_class->get_typed_referring_object_list = impl_get_typed_referring_object_list;
368 
369     g_object_class_install_property
370     (gobject_class,
371      PROP_NAME,
372      g_param_spec_string ("name",
373                           "TaxTable Name",
374                           "The accountName is an arbitrary string "
375                           "assigned by the user.  It is intended to "
376                           "a short, 10 to 30 character long string "
377                           "that is displayed by the GUI as the "
378                           "tax table mnemonic.",
379                           NULL,
380                           G_PARAM_READWRITE));
381 
382     g_object_class_install_property
383     (gobject_class,
384      PROP_INVISIBLE,
385      g_param_spec_boolean ("invisible",
386                            "Invisible",
387                            "TRUE if the tax table is invisible.  FALSE if visible.",
388                            FALSE,
389                            G_PARAM_READWRITE));
390 
391     g_object_class_install_property
392     (gobject_class,
393      PROP_REFCOUNT,
394      g_param_spec_uint64("ref-count",
395                          "Reference count",
396                          "The ref-count property contains number of times this tax table "
397                          "is referenced.",
398                          0,           /* min */
399                          G_MAXUINT64, /* max */
400                          0,           /* default */
401                          G_PARAM_READWRITE));
402 }
403 
404 /* Create/Destroy Functions */
405 GncTaxTable *
gncTaxTableCreate(QofBook * book)406 gncTaxTableCreate (QofBook *book)
407 {
408     GncTaxTable *table;
409     if (!book) return NULL;
410 
411     table = g_object_new (GNC_TYPE_TAXTABLE, NULL);
412     qof_instance_init_data (&table->inst, _GNC_MOD_NAME, book);
413     table->name = CACHE_INSERT ("");
414     addObj (table);
415     qof_event_gen (&table->inst, QOF_EVENT_CREATE, NULL);
416     return table;
417 }
418 
419 void
gncTaxTableDestroy(GncTaxTable * table)420 gncTaxTableDestroy (GncTaxTable *table)
421 {
422     if (!table) return;
423     qof_instance_set_destroying(table, TRUE);
424     qof_instance_set_dirty (&table->inst);
425     gncTaxTableCommitEdit (table);
426 }
427 
428 static void
gncTaxTableFree(GncTaxTable * table)429 gncTaxTableFree (GncTaxTable *table)
430 {
431     GList *list;
432     GncTaxTable *child;
433 
434     if (!table) return;
435 
436     qof_event_gen (&table->inst, QOF_EVENT_DESTROY, NULL);
437     CACHE_REMOVE (table->name);
438     remObj (table);
439 
440     /* destroy the list of entries */
441     for (list = table->entries; list; list = list->next)
442         gncTaxTableEntryDestroy (list->data);
443     g_list_free (table->entries);
444 
445     if (!qof_instance_get_destroying(table))
446         PERR("free a taxtable without do_free set!");
447 
448     /* disconnect from parent */
449     if (table->parent)
450         gncTaxTableRemoveChild(table->parent, table);
451 
452     /* disconnect from the children */
453     for (list = table->children; list; list = list->next)
454     {
455         child = list->data;
456         gncTaxTableSetParent(child, NULL);
457     }
458     g_list_free(table->children);
459 
460     /* qof_instance_release (&table->inst); */
461     g_object_unref (table);
462 }
463 
464 /* =============================================================== */
465 
gncTaxTableEntryCreate(void)466 GncTaxTableEntry * gncTaxTableEntryCreate (void)
467 {
468     GncTaxTableEntry *entry;
469     entry = g_new0 (GncTaxTableEntry, 1);
470     entry->amount = gnc_numeric_zero ();
471     return entry;
472 }
473 
gncTaxTableEntryDestroy(GncTaxTableEntry * entry)474 void gncTaxTableEntryDestroy (GncTaxTableEntry *entry)
475 {
476     if (!entry) return;
477     g_free (entry);
478 }
479 
480 /* =============================================================== */
481 /* Set Functions */
482 
gncTaxTableSetName(GncTaxTable * table,const char * name)483 void gncTaxTableSetName (GncTaxTable *table, const char *name)
484 {
485     if (!table || !name) return;
486     SET_STR (table, table->name, name);
487     mark_table (table);
488     maybe_resort_list (table);
489     gncTaxTableCommitEdit (table);
490 }
491 
gncTaxTableSetParent(GncTaxTable * table,GncTaxTable * parent)492 void gncTaxTableSetParent (GncTaxTable *table, GncTaxTable *parent)
493 {
494     if (!table) return;
495     gncTaxTableBeginEdit (table);
496     if (table->parent)
497         gncTaxTableRemoveChild(table->parent, table);
498     table->parent = parent;
499     if (parent)
500         gncTaxTableAddChild(parent, table);
501     table->refcount = 0;
502     gncTaxTableMakeInvisible (table);
503     mark_table (table);
504     gncTaxTableCommitEdit (table);
505 }
506 
gncTaxTableSetChild(GncTaxTable * table,GncTaxTable * child)507 void gncTaxTableSetChild (GncTaxTable *table, GncTaxTable *child)
508 {
509     if (!table) return;
510     gncTaxTableBeginEdit (table);
511     table->child = child;
512     mark_table (table);
513     gncTaxTableCommitEdit (table);
514 }
515 
gncTaxTableIncRef(GncTaxTable * table)516 void gncTaxTableIncRef (GncTaxTable *table)
517 {
518     if (!table) return;
519     if (table->parent || table->invisible) return;        /* children dont need refcounts */
520     gncTaxTableBeginEdit (table);
521     table->refcount++;
522     mark_table (table);
523     gncTaxTableCommitEdit (table);
524 }
525 
gncTaxTableDecRef(GncTaxTable * table)526 void gncTaxTableDecRef (GncTaxTable *table)
527 {
528     if (!table) return;
529     if (table->parent || table->invisible) return;        /* children dont need refcounts */
530     g_return_if_fail (table->refcount > 0);
531     gncTaxTableBeginEdit (table);
532     table->refcount--;
533     mark_table (table);
534     gncTaxTableCommitEdit (table);
535 }
536 
gncTaxTableSetRefcount(GncTaxTable * table,gint64 refcount)537 void gncTaxTableSetRefcount (GncTaxTable *table, gint64 refcount)
538 {
539     if (!table) return;
540     g_return_if_fail (refcount >= 0);
541     gncTaxTableBeginEdit (table);
542     table->refcount = refcount;
543     mark_table (table);
544     gncTaxTableCommitEdit (table);
545 }
546 
gncTaxTableMakeInvisible(GncTaxTable * table)547 void gncTaxTableMakeInvisible (GncTaxTable *table)
548 {
549     struct _book_info *bi;
550     if (!table) return;
551     gncTaxTableBeginEdit (table);
552     table->invisible = TRUE;
553     bi = qof_book_get_data (qof_instance_get_book(table), _GNC_MOD_NAME);
554     bi->tables = g_list_remove (bi->tables, table);
555     gncTaxTableCommitEdit (table);
556 }
557 
gncTaxTableEntrySetAccount(GncTaxTableEntry * entry,Account * account)558 void gncTaxTableEntrySetAccount (GncTaxTableEntry *entry, Account *account)
559 {
560     if (!entry || !account) return;
561     if (entry->account == account) return;
562     entry->account = account;
563     if (entry->table)
564     {
565         mark_table (entry->table);
566         mod_table (entry->table);
567     }
568 }
569 
gncTaxTableEntrySetType(GncTaxTableEntry * entry,GncAmountType type)570 void gncTaxTableEntrySetType (GncTaxTableEntry *entry, GncAmountType type)
571 {
572     if (!entry) return;
573     if (entry->type == type) return;
574     entry->type = type;
575     if (entry->table)
576     {
577         mark_table (entry->table);
578         mod_table (entry->table);
579     }
580 }
581 
gncTaxTableEntrySetAmount(GncTaxTableEntry * entry,gnc_numeric amount)582 void gncTaxTableEntrySetAmount (GncTaxTableEntry *entry, gnc_numeric amount)
583 {
584     if (!entry) return;
585     if (gnc_numeric_eq (entry->amount, amount)) return;
586     entry->amount = amount;
587     if (entry->table)
588     {
589         mark_table (entry->table);
590         mod_table (entry->table);
591     }
592 }
593 
gncTaxTableAddEntry(GncTaxTable * table,GncTaxTableEntry * entry)594 void gncTaxTableAddEntry (GncTaxTable *table, GncTaxTableEntry *entry)
595 {
596     if (!table || !entry) return;
597     if (entry->table == table) return; /* already mine */
598 
599     gncTaxTableBeginEdit (table);
600     if (entry->table)
601         gncTaxTableRemoveEntry (entry->table, entry);
602 
603     entry->table = table;
604     table->entries = g_list_insert_sorted (table->entries, entry,
605                                            (GCompareFunc)gncTaxTableEntryCompare);
606     mark_table (table);
607     mod_table (table);
608     gncTaxTableCommitEdit (table);
609 }
610 
gncTaxTableRemoveEntry(GncTaxTable * table,GncTaxTableEntry * entry)611 void gncTaxTableRemoveEntry (GncTaxTable *table, GncTaxTableEntry *entry)
612 {
613     if (!table || !entry) return;
614     gncTaxTableBeginEdit (table);
615     entry->table = NULL;
616     table->entries = g_list_remove (table->entries, entry);
617     mark_table (table);
618     mod_table (table);
619     gncTaxTableCommitEdit (table);
620 }
621 
gncTaxTableChanged(GncTaxTable * table)622 void gncTaxTableChanged (GncTaxTable *table)
623 {
624     if (!table) return;
625     gncTaxTableBeginEdit (table);
626     table->child = NULL;
627     gncTaxTableCommitEdit (table);
628 }
629 
630 /* =============================================================== */
631 
gncTaxTableBeginEdit(GncTaxTable * table)632 void gncTaxTableBeginEdit (GncTaxTable *table)
633 {
634     qof_begin_edit(&table->inst);
635 }
636 
gncTaxTableOnError(QofInstance * inst,QofBackendError errcode)637 static void gncTaxTableOnError (QofInstance *inst, QofBackendError errcode)
638 {
639     PERR("TaxTable QofBackend Failure: %d", errcode);
640     gnc_engine_signal_commit_error( errcode );
641 }
642 
gncTaxTableOnDone(QofInstance * inst)643 static void gncTaxTableOnDone (QofInstance *inst) {}
644 
table_free(QofInstance * inst)645 static void table_free (QofInstance *inst)
646 {
647     GncTaxTable *table = (GncTaxTable *) inst;
648     gncTaxTableFree (table);
649 }
650 
gncTaxTableCommitEdit(GncTaxTable * table)651 void gncTaxTableCommitEdit (GncTaxTable *table)
652 {
653     /* GnuCash 2.6.3 and earlier didn't handle taxtable kvp's... */
654      if (qof_instance_has_kvp (QOF_INSTANCE (table)))
655           gnc_features_set_used (qof_instance_get_book (QOF_INSTANCE (table)),
656                                  GNC_FEATURE_KVP_EXTRA_DATA);
657 
658     if (!qof_commit_edit (QOF_INSTANCE(table))) return;
659     qof_commit_edit_part2 (&table->inst, gncTaxTableOnError,
660                            gncTaxTableOnDone, table_free);
661 }
662 
663 
664 /* =============================================================== */
665 /* Get Functions */
666 
gncTaxTableLookupByName(QofBook * book,const char * name)667 GncTaxTable *gncTaxTableLookupByName (QofBook *book, const char *name)
668 {
669     GList *list = gncTaxTableGetTables (book);
670 
671     for ( ; list; list = list->next)
672     {
673         GncTaxTable *table = list->data;
674         if (!g_strcmp0 (table->name, name))
675             return list->data;
676     }
677     return NULL;
678 }
679 
680 GncTaxTable*
gncTaxTableGetDefault(QofBook * book,GncOwnerType type)681 gncTaxTableGetDefault (QofBook *book, GncOwnerType type)
682 {
683     GSList *path = NULL;
684     const GncGUID *guid = NULL;
685     const char *vendor = "Default Vendor TaxTable";
686     const char *customer = "Default Customer TaxTable";
687     const char *section = "Business";
688 
689     g_return_val_if_fail (book != NULL, NULL);
690     g_return_val_if_fail (type == GNC_OWNER_CUSTOMER || \
691                           type == GNC_OWNER_VENDOR, NULL);
692     path = g_slist_prepend (path, type == GNC_OWNER_CUSTOMER ? (void*)customer : (void*)vendor);
693     path = g_slist_prepend (path, (void*)section);
694 
695     guid = qof_book_get_guid_option (book, path);
696     g_slist_free (path);
697 
698     return gncTaxTableLookup (book, guid);
699 }
700 
gncTaxTableGetTables(QofBook * book)701 GncTaxTableList * gncTaxTableGetTables (QofBook *book)
702 {
703     struct _book_info *bi;
704     if (!book) return NULL;
705 
706     bi = qof_book_get_data (book, _GNC_MOD_NAME);
707     return bi ? bi->tables : NULL;
708 }
709 
gncTaxTableGetName(const GncTaxTable * table)710 const char *gncTaxTableGetName (const GncTaxTable *table)
711 {
712     if (!table) return NULL;
713     return table->name;
714 }
715 
gncTaxTableEntryCopy(const GncTaxTableEntry * entry)716 static GncTaxTableEntry *gncTaxTableEntryCopy (const GncTaxTableEntry *entry)
717 {
718     GncTaxTableEntry *e;
719     if (!entry) return NULL;
720 
721     e = gncTaxTableEntryCreate ();
722     gncTaxTableEntrySetAccount (e, entry->account);
723     gncTaxTableEntrySetType (e, entry->type);
724     gncTaxTableEntrySetAmount (e, entry->amount);
725 
726     return e;
727 }
728 
gncTaxTableCopy(const GncTaxTable * table)729 static GncTaxTable *gncTaxTableCopy (const GncTaxTable *table)
730 {
731     GncTaxTable *t;
732     GList *list;
733 
734     if (!table) return NULL;
735     t = gncTaxTableCreate (qof_instance_get_book(table));
736     gncTaxTableSetName (t, table->name);
737     for (list = table->entries; list; list = list->next)
738     {
739         GncTaxTableEntry *entry, *e;
740         entry = list->data;
741         e = gncTaxTableEntryCopy (entry);
742        /* Clang static analyzer thinks we're leaking e, but we're not.
743         * We're transferring it to table. */
744         gncTaxTableAddEntry (t, e);
745     }
746     return t;
747 }
748 
gncTaxTableReturnChild(GncTaxTable * table,gboolean make_new)749 GncTaxTable *gncTaxTableReturnChild (GncTaxTable *table, gboolean make_new)
750 {
751     GncTaxTable *child = NULL;
752 
753     if (!table) return NULL;
754     if (table->child) return table->child;
755     if (table->parent || table->invisible) return table;
756     if (make_new)
757     {
758         child = gncTaxTableCopy (table);
759         gncTaxTableSetChild (table, child);
760         gncTaxTableSetParent (child, table);
761     }
762     return child;
763 }
764 
gncTaxTableGetParent(const GncTaxTable * table)765 GncTaxTable *gncTaxTableGetParent (const GncTaxTable *table)
766 {
767     if (!table) return NULL;
768     return table->parent;
769 }
770 
gncTaxTableGetEntries(const GncTaxTable * table)771 GncTaxTableEntryList* gncTaxTableGetEntries (const GncTaxTable *table)
772 {
773     if (!table) return NULL;
774     return table->entries;
775 }
776 
gncTaxTableGetRefcount(const GncTaxTable * table)777 gint64 gncTaxTableGetRefcount (const GncTaxTable *table)
778 {
779     if (!table) return 0;
780     return table->refcount;
781 }
782 
gncTaxTableLastModifiedSecs(const GncTaxTable * table)783 time64 gncTaxTableLastModifiedSecs (const GncTaxTable *table)
784 {
785     if (!table) return 0;
786     return table->modtime;
787 }
788 
gncTaxTableGetInvisible(const GncTaxTable * table)789 gboolean gncTaxTableGetInvisible (const GncTaxTable *table)
790 {
791     if (!table) return FALSE;
792     return table->invisible;
793 }
794 
gncTaxTableEntryGetAccount(const GncTaxTableEntry * entry)795 Account * gncTaxTableEntryGetAccount (const GncTaxTableEntry *entry)
796 {
797     if (!entry) return NULL;
798     return entry->account;
799 }
800 
gncTaxTableEntryGetType(const GncTaxTableEntry * entry)801 GncAmountType gncTaxTableEntryGetType (const GncTaxTableEntry *entry)
802 {
803     if (!entry) return 0;
804     return entry->type;
805 }
806 
gncTaxTableEntryGetAmount(const GncTaxTableEntry * entry)807 gnc_numeric gncTaxTableEntryGetAmount (const GncTaxTableEntry *entry)
808 {
809     if (!entry) return gnc_numeric_zero();
810     return entry->amount;
811 }
812 
813 /* This is a semi-private function (meaning that it's not declared in
814  * the header) used for SQL Backend testing. */
gncTaxTableEntryGetTable(const GncTaxTableEntry * entry)815 GncTaxTable* gncTaxTableEntryGetTable( const GncTaxTableEntry* entry )
816 {
817     if (!entry) return NULL;
818     return entry->table;
819 }
820 
gncTaxTableEntryCompare(const GncTaxTableEntry * a,const GncTaxTableEntry * b)821 int gncTaxTableEntryCompare (const GncTaxTableEntry *a, const GncTaxTableEntry *b)
822 {
823     char *name_a, *name_b;
824     int retval;
825 
826     if (!a && !b) return 0;
827     if (!a) return -1;
828     if (!b) return 1;
829 
830     name_a = gnc_account_get_full_name (a->account);
831     name_b = gnc_account_get_full_name (b->account);
832     retval = g_strcmp0(name_a, name_b);
833     g_free(name_a);
834     g_free(name_b);
835 
836     if (retval)
837         return retval;
838 
839     return gnc_numeric_compare (a->amount, b->amount);
840 }
841 
gncTaxTableCompare(const GncTaxTable * a,const GncTaxTable * b)842 int gncTaxTableCompare (const GncTaxTable *a, const GncTaxTable *b)
843 {
844     if (!a && !b) return 0;
845     if (!a) return -1;
846     if (!b) return 1;
847     return g_strcmp0 (a->name, b->name);
848 }
849 
gncTaxTableEntryEqual(const GncTaxTableEntry * a,const GncTaxTableEntry * b)850 gboolean gncTaxTableEntryEqual(const GncTaxTableEntry *a, const GncTaxTableEntry *b)
851 {
852     if (a == NULL && b == NULL) return TRUE;
853     if (a == NULL || b == NULL) return FALSE;
854 
855     if (!xaccAccountEqual(a->account, b->account, TRUE))
856     {
857         PWARN("accounts differ");
858         return FALSE;
859     }
860 
861     if (a->type != b->type)
862     {
863         PWARN("types differ");
864         return FALSE;
865     }
866 
867     if (!gnc_numeric_equal(a->amount, b->amount))
868     {
869         PWARN("amounts differ");
870         return FALSE;
871     }
872 
873     return TRUE;
874 }
875 
gncTaxTableEqual(const GncTaxTable * a,const GncTaxTable * b)876 gboolean gncTaxTableEqual(const GncTaxTable *a, const GncTaxTable *b)
877 {
878     if (a == NULL && b == NULL) return TRUE;
879     if (a == NULL || b == NULL) return FALSE;
880 
881     g_return_val_if_fail(GNC_IS_TAXTABLE(a), FALSE);
882     g_return_val_if_fail(GNC_IS_TAXTABLE(b), FALSE);
883 
884     if (g_strcmp0(a->name, b->name) != 0)
885     {
886         PWARN("Names differ: %s vs %s", a->name, b->name);
887         return FALSE;
888     }
889 
890     if (a->invisible != b->invisible)
891     {
892         PWARN("invisible flags differ");
893         return FALSE;
894     }
895 
896     if ((a->entries != NULL) != (b->entries != NULL))
897     {
898         PWARN("only one has entries");
899         return FALSE;
900     }
901 
902     if (a->entries != NULL && b->entries != NULL)
903     {
904         GncTaxTableEntryList* a_node;
905         GncTaxTableEntryList* b_node;
906 
907         for (a_node = a->entries, b_node = b->entries;
908                 a_node != NULL && b_node != NULL;
909                 a_node = a_node->next, b_node = b_node->next)
910         {
911             if (!gncTaxTableEntryEqual((GncTaxTableEntry*)a_node->data,
912                                        (GncTaxTableEntry*)b_node->data))
913             {
914                 PWARN("entries differ");
915                 return FALSE;
916             }
917         }
918 
919         if (a_node != NULL || b_node != NULL)
920         {
921             PWARN("Unequal number of entries");
922             return FALSE;
923         }
924     }
925 
926 #if 0
927     /* See src/doc/business.txt for an explanation of the following */
928     /* Code that handles this is *identical* to that in gncBillTerm */
929     gint64          refcount;
930     GncTaxTable *   parent;       /* if non-null, we are an immutable child */
931     GncTaxTable *   child;        /* if non-null, we have not changed */
932     GList *         children;     /* list of children for disconnection */
933 #endif
934 
935     return TRUE;
936 }
937 
938 /*
939  * This will add value to the account-value for acc, creating a new
940  * list object if necessary
941  */
gncAccountValueAdd(GList * list,Account * acc,gnc_numeric value)942 GList *gncAccountValueAdd (GList *list, Account *acc, gnc_numeric value)
943 {
944     GList *li;
945     GncAccountValue *res = NULL;
946 
947     g_return_val_if_fail (acc, list);
948     g_return_val_if_fail (gnc_numeric_check (value) == GNC_ERROR_OK, list);
949 
950     /* Try to find the account in the list */
951     for (li = list; li; li = li->next)
952     {
953         res = li->data;
954         if (res->account == acc)
955         {
956             res->value = gnc_numeric_add (res->value, value, GNC_DENOM_AUTO,
957                                           GNC_HOW_DENOM_REDUCE | GNC_HOW_RND_ROUND_HALF_UP);
958             return list;
959         }
960     }
961     /* Nope, didn't find it. */
962 
963     res = g_new0 (GncAccountValue, 1);
964     res->account = acc;
965     res->value = value;
966     return g_list_prepend (list, res);
967 }
968 
969 /* Merge l2 into l1.  l2 is not touched. */
gncAccountValueAddList(GList * l1,GList * l2)970 GList *gncAccountValueAddList (GList *l1, GList *l2)
971 {
972     GList *li;
973 
974     for (li = l2; li; li = li->next )
975     {
976         GncAccountValue *val = li->data;
977         l1 = gncAccountValueAdd (l1, val->account, val->value);
978     }
979 
980     return l1;
981 }
982 
983 /* return the total for this list */
gncAccountValueTotal(GList * list)984 gnc_numeric gncAccountValueTotal (GList *list)
985 {
986     gnc_numeric total = gnc_numeric_zero ();
987 
988     for ( ; list ; list = list->next)
989     {
990         GncAccountValue *val = list->data;
991         total = gnc_numeric_add (total, val->value, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE | GNC_HOW_RND_ROUND_HALF_UP);
992     }
993     return total;
994 }
995 
996 /* Destroy a list of accountvalues */
gncAccountValueDestroy(GList * list)997 void gncAccountValueDestroy (GList *list)
998 {
999     GList *node;
1000     for ( node = list; node ; node = node->next)
1001         g_free (node->data);
1002 
1003     g_list_free (list);
1004 }
1005 
1006 /* Package-Private functions */
1007 
_gncTaxTableCreate(QofBook * book)1008 static void _gncTaxTableCreate (QofBook *book)
1009 {
1010     struct _book_info *bi;
1011 
1012     if (!book) return;
1013 
1014     bi = g_new0 (struct _book_info, 1);
1015     qof_book_set_data (book, _GNC_MOD_NAME, bi);
1016 }
1017 
_gncTaxTableDestroy(QofBook * book)1018 static void _gncTaxTableDestroy (QofBook *book)
1019 {
1020     struct _book_info *bi;
1021 
1022     if (!book) return;
1023 
1024     bi = qof_book_get_data (book, _GNC_MOD_NAME);
1025 
1026     g_list_free (bi->tables);
1027     g_free (bi);
1028 }
1029 
1030 static QofObject gncTaxTableDesc =
1031 {
1032     DI(.interface_version = ) QOF_OBJECT_VERSION,
1033     DI(.e_type            = ) _GNC_MOD_NAME,
1034     DI(.type_label        = ) "Tax Table",
1035     DI(.create            = ) (gpointer)gncTaxTableCreate,
1036     DI(.book_begin        = ) _gncTaxTableCreate,
1037     DI(.book_end          = ) _gncTaxTableDestroy,
1038     DI(.is_dirty          = ) qof_collection_is_dirty,
1039     DI(.mark_clean        = ) qof_collection_mark_clean,
1040     DI(.foreach           = ) qof_collection_foreach,
1041     DI(.printable         = ) NULL,
1042     DI(.version_cmp       = ) (int (*)(gpointer, gpointer)) qof_instance_version_cmp,
1043 };
1044 
gncTaxTableRegister(void)1045 gboolean gncTaxTableRegister (void)
1046 {
1047     static QofParam params[] =
1048     {
1049         { GNC_TT_NAME, 		QOF_TYPE_STRING, 	(QofAccessFunc)gncTaxTableGetName, 		(QofSetterFunc)gncTaxTableSetName },
1050         { GNC_TT_REFCOUNT, 	QOF_TYPE_INT64, 	(QofAccessFunc)gncTaxTableGetRefcount, 	(QofSetterFunc)gncTaxTableSetRefcount },
1051         { QOF_PARAM_BOOK, 	QOF_ID_BOOK, 		(QofAccessFunc)qof_instance_get_book, 	NULL },
1052         { QOF_PARAM_GUID, 	QOF_TYPE_GUID, 		(QofAccessFunc)qof_instance_get_guid, 	NULL },
1053         { NULL },
1054     };
1055 
1056     qof_class_register (_GNC_MOD_NAME, (QofSortFunc)gncTaxTableCompare, params);
1057 
1058     return qof_object_register (&gncTaxTableDesc);
1059 }
1060 
1061 /* need a QOF tax table entry object */
1062 //gncTaxTableEntrySetType_q int32
1063 //gint gncTaxTableEntryGetType_q (GncTaxTableEntry *entry);
1064