* src/backend/file/io-gncxml-v2.[ch]: add API to the plugin

modules to allow post-processing (scrubbing) of the book after
	  it is loaded from XML.  This allows a plug-in to post-process
	  the complete book.
	* src/business/business-core/file/gnc-customer-xml-v2.c:
	* src/business/business-core/file/gnc-employee-xml-v2.c:
	* src/business/business-core/file/gnc-entry-xml-v2.c:
	* src/business/business-core/file/gnc-invoice-xml-v2.c:
	* src/business/business-core/file/gnc-job-xml-v2.c:
	* src/business/business-core/file/gnc-order-xml-v2.c:
	* src/business/business-core/file/gnc-vendor-xml-v2.c:
	  add a NULL scrub member to the XML plugin API.
	* src/business/business-core/file/gnc-tax-table-xml-v2.c:
	  create a scrub function to clear up bogus tax tables due
	  to bug #114888 (and related bugs) which could cause
	  tax tables to get created ad-nausium if you post and then
	  unpost an invoice.  Scrubs all the entries for bogus
	  tax tables, and then clears out the bogus tax tables.
	* src/business/business-core/file/gnc-bill-term-xml-v2.c:
	  Hook in an empty scrub routine, because I think the same
	  bug exists here, but I'll work on that later.
	* src/business/business-core/gncEntry.c:
	  make sure we properly dereference tax tables when we destroy
	  an entry.
	* src/business/business-core/gncTaxTable.c:
	  - keep a list of children so we can unref ourselves from our
	    children when we get destroyed
	  - deal with parentless children
	Fixes bug #114888 -- don't generate phantom (parent) tax tables
	and clean up any existing bogus tax tables.


git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@8595 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
Derek Atkins 2003-06-14 04:31:03 +00:00
parent 21edb97a57
commit d2c193825a
14 changed files with 306 additions and 5 deletions

View File

@ -1,3 +1,36 @@
2003-06-13 Derek Atkins <derek@ihtfp.com>
* src/backend/file/io-gncxml-v2.[ch]: add API to the plugin
modules to allow post-processing (scrubbing) of the book after
it is loaded from XML. This allows a plug-in to post-process
the complete book.
* src/business/business-core/file/gnc-customer-xml-v2.c:
* src/business/business-core/file/gnc-employee-xml-v2.c:
* src/business/business-core/file/gnc-entry-xml-v2.c:
* src/business/business-core/file/gnc-invoice-xml-v2.c:
* src/business/business-core/file/gnc-job-xml-v2.c:
* src/business/business-core/file/gnc-order-xml-v2.c:
* src/business/business-core/file/gnc-vendor-xml-v2.c:
add a NULL scrub member to the XML plugin API.
* src/business/business-core/file/gnc-tax-table-xml-v2.c:
create a scrub function to clear up bogus tax tables due
to bug #114888 (and related bugs) which could cause
tax tables to get created ad-nausium if you post and then
unpost an invoice. Scrubs all the entries for bogus
tax tables, and then clears out the bogus tax tables.
* src/business/business-core/file/gnc-bill-term-xml-v2.c:
Hook in an empty scrub routine, because I think the same
bug exists here, but I'll work on that later.
* src/business/business-core/gncEntry.c:
make sure we properly dereference tax tables when we destroy
an entry.
* src/business/business-core/gncTaxTable.c:
- keep a list of children so we can unref ourselves from our
children when we get destroyed
- deal with parentless children
Fixes bug #114888 -- don't generate phantom (parent) tax tables
and clean up any existing bogus tax tables.
2003-06-12 Christian Stimming <stimming@tuhh.de>
* doc/TRANSLATION_HOWTO: Added, by Jon Lapham

View File

@ -591,6 +591,19 @@ add_parser_cb (const char *type, gpointer data_p, gpointer be_data_p)
be_data->ok = FALSE;
}
static void
scrub_cb (const char *type, gpointer data_p, gpointer be_data_p)
{
GncXmlDataType_t *data = data_p;
struct file_backend *be_data = be_data_p;
g_return_if_fail (type && data && be_data);
g_return_if_fail (data->version == GNC_FILE_BACKEND_VERS);
if (data->scrub)
(data->scrub)(be_data->book);
}
static sixtp_gdv2 *
gnc_sixtp_gdv2_new (
GNCBook *book,
@ -704,6 +717,11 @@ gnc_session_load_from_xml_file_v2(GNCSession *session)
/* Mark the book as saved */
gnc_book_mark_saved (book);
/* Call individual scrub functions */
memset(&be_data, 0, sizeof(be_data));
be_data.book = book;
gncObjectForeachBackend (GNC_FILE_BACKEND, scrub_cb, &be_data);
grp = gnc_book_get_group(book);
/* fix price quote sources */
xaccGroupScrubQuoteSources (grp, gnc_book_get_commodity_table(book));

View File

@ -93,6 +93,9 @@ struct sixtp_gdv2
*
* The write() method writes out all the objects of this particular type
* in the book and stores the XML in the FILE.
*
* The scrub() method will take a completed, parsed GNCBook* and post process
* the data, allowing you to 'scrub' the data.
*/
#define GNC_FILE_BACKEND "gnc:file:2"
#define GNC_FILE_BACKEND_VERS 2
@ -105,6 +108,7 @@ typedef struct
gboolean (*add_item)(sixtp_gdv2 *, gpointer obj);
int (*get_count) (GNCBook *);
void (*write) (FILE*, GNCBook*);
void (*scrub) (GNCBook *);
} GncXmlDataType_t;
/**

View File

@ -515,6 +515,12 @@ billterm_write (FILE *out, GNCBook *book)
gncObjectForeach (_GNC_MOD_NAME, book, xml_add_billterm, (gpointer) out);
}
static void
billterm_scrub (GNCBook *book)
{
}
void
gnc_billterm_xml_initialize (void)
{
@ -525,6 +531,7 @@ gnc_billterm_xml_initialize (void)
NULL, /* add_item */
billterm_get_count,
billterm_write,
billterm_scrub,
};
gncObjectRegisterBackend (_GNC_MOD_NAME,

View File

@ -517,6 +517,7 @@ gnc_customer_xml_initialize (void)
NULL, /* add_item */
customer_get_count,
customer_write,
NULL, /* scrub */
};
gncObjectRegisterBackend (_GNC_MOD_NAME,

View File

@ -431,6 +431,7 @@ gnc_employee_xml_initialize (void)
NULL, /* add_item */
employee_get_count,
employee_write,
NULL, /* scrub */
};
gncObjectRegisterBackend (_GNC_MOD_NAME,

View File

@ -825,6 +825,7 @@ gnc_entry_xml_initialize (void)
NULL, /* add_item */
entry_get_count,
entry_write,
NULL, /* scrub */
};
gncObjectRegisterBackend (_GNC_MOD_NAME,

View File

@ -551,6 +551,7 @@ gnc_invoice_xml_initialize (void)
NULL, /* add_item */
invoice_get_count,
invoice_write,
NULL, /* scrub */
};
gncObjectRegisterBackend (_GNC_MOD_NAME,

View File

@ -336,6 +336,7 @@ gnc_job_xml_initialize (void)
NULL, /* add_item */
job_get_count,
job_write,
NULL, /* scrub */
};
gncObjectRegisterBackend (_GNC_MOD_NAME,

View File

@ -374,6 +374,7 @@ gnc_order_xml_initialize (void)
NULL, /* add_item */
order_get_count,
order_write,
NULL, /* scrub */
};
gncObjectRegisterBackend (_GNC_MOD_NAME,

View File

@ -46,6 +46,7 @@
#include "gnc-engine-util.h"
#include "gncObject.h"
#include "gncEntry.h"
#define _GNC_MOD_NAME GNC_TAXTABLE_MODULE_NAME
@ -117,7 +118,10 @@ taxtable_dom_tree_create (GncTaxTable *table)
xmlAddChild(ret, int_to_dom_tree (taxtable_invisible_string,
gncTaxTableGetInvisible (table)));
maybe_add_guid(ret, taxtable_child_string, gncTaxTableGetChild (table));
/* We should not be our own child */
if (gncTaxTableGetChild(table) != table)
maybe_add_guid(ret, taxtable_child_string, gncTaxTableGetChild (table));
maybe_add_guid(ret, taxtable_parent_string, gncTaxTableGetParent (table));
entries = xmlNewChild (ret, NULL, taxtable_entries_string, NULL);
@ -232,6 +236,13 @@ set_parent_child (xmlNodePtr node, struct taxtable_pdata *pdata,
guid = dom_tree_to_guid(node);
g_return_val_if_fail (guid, FALSE);
table = gncTaxTableLookup (pdata->book, guid);
/* Ignore pointers to self */
if (table == pdata->table) {
PINFO ("found a self-referential parent/child; ignoring.\n");
return TRUE;
}
if (!table) {
table = gncTaxTableCreate (pdata->book);
gncTaxTableBeginEdit (table);
@ -461,6 +472,181 @@ taxtable_write (FILE *out, GNCBook *book)
gncObjectForeach (_GNC_MOD_NAME, book, xml_add_taxtable, (gpointer) out);
}
static gboolean
taxtable_is_grandchild (GncTaxTable *table)
{
return (gncTaxTableGetParent(gncTaxTableGetParent(table)) != NULL);
}
static GncTaxTable *
taxtable_find_senior (GncTaxTable *table)
{
GncTaxTable *temp, *parent, *gp = NULL;
temp = table;
do {
/* See if "temp" is a grandchild */
parent = gncTaxTableGetParent(temp);
if (!parent)
break;
gp = gncTaxTableGetParent(parent);
if (!gp)
break;
/* Yep, this is a grandchild. Move up one generation and try again */
temp = parent;
} while (TRUE);
/* Ok, at this point temp points to the most senior child and parent
* should point to the top taxtable (and gp should be NULL). If
* parent is NULL then we are the most senior child (and have no
* children), so do nothing. If temp == table then there is no
* grandparent, so do nothing.
*
* Do something if parent != NULL && temp != table
*/
g_assert (gp == NULL);
/* return the most senior table */
return temp;
}
/* build a list of tax tables that are grandchildren or bogus (empty entry list). */
static void
taxtable_scrub_cb (gpointer table_p, gpointer list_p)
{
GncTaxTable *table = table_p;
GList **list = list_p;
if (taxtable_is_grandchild(table) || gncTaxTableGetEntries(table) == NULL)
*list = g_list_prepend(*list, table);
}
/* for each entry, check the tax tables. If the tax tables are
* grandchildren, then fix them to point to the most senior child
*/
static void
taxtable_scrub_entries (gpointer entry_p, gpointer ht_p)
{
GHashTable *ht = ht_p;
GncEntry *entry = entry_p;
GncTaxTable *table, *new_tt;
gint32 count;
table = gncEntryGetInvTaxTable(entry);
if (table) {
count = GPOINTER_TO_INT(g_hash_table_lookup(ht, table));
count++;
g_hash_table_insert(ht, table, GINT_TO_POINTER(count));
if (taxtable_is_grandchild(table)) {
PINFO("Fixing i-taxtable on entry %s\n",
guid_to_string(gncEntryGetGUID(entry)));
new_tt = taxtable_find_senior(table);
gncEntryBeginEdit(entry);
gncEntrySetInvTaxTable(entry, new_tt);
gncEntryCommitEdit(entry);
}
}
table = gncEntryGetBillTaxTable(entry);
if (table) {
count = GPOINTER_TO_INT(g_hash_table_lookup(ht, table));
count++;
g_hash_table_insert(ht, table, GINT_TO_POINTER(count));
if (taxtable_is_grandchild(table)) {
PINFO("Fixing b-taxtable on entry %s\n",
guid_to_string(gncEntryGetGUID(entry)));
new_tt = taxtable_find_senior(table);
gncEntryBeginEdit(entry);
gncEntrySetBillTaxTable(entry, new_tt);
gncEntryCommitEdit(entry);
}
}
}
static void
taxtable_scrub_cust (gpointer cust_p, gpointer ht_p)
{
GHashTable *ht = ht_p;
GncCustomer *cust = cust_p;
GncTaxTable *table;
gint32 count;
table = gncCustomerGetTaxTable(cust);
if (table) {
count = GPOINTER_TO_INT(g_hash_table_lookup(ht, table));
count++;
g_hash_table_insert(ht, table, GINT_TO_POINTER(count));
}
}
static void
taxtable_scrub_vendor (gpointer vendor_p, gpointer ht_p)
{
GHashTable *ht = ht_p;
GncVendor *vendor = vendor_p;
GncTaxTable *table;
gint32 count;
table = gncVendorGetTaxTable(vendor);
if (table) {
count = GPOINTER_TO_INT(g_hash_table_lookup(ht, table));
count++;
g_hash_table_insert(ht, table, GINT_TO_POINTER(count));
}
}
static void
taxtable_reset_refcount (gpointer key, gpointer value, gpointer notused)
{
GncTaxTable *table = key;
gint32 count = GPOINTER_TO_INT(value);
if (count != gncTaxTableGetRefcount(table) && !gncTaxTableGetInvisible(table)) {
PWARN("Fixing refcount on taxtable %s (%lld -> %d)\n",
guid_to_string(gncTaxTableGetGUID(table)),
gncTaxTableGetRefcount(table), count)
gncTaxTableSetRefcount(table, count);
}
}
static void
taxtable_scrub (GNCBook *book)
{
GList *list = NULL;
GList *node;
GncTaxTable *parent, *table;
GHashTable *ht = g_hash_table_new(g_direct_hash, g_direct_equal);
gncObjectForeach (GNC_ENTRY_MODULE_NAME, book, taxtable_scrub_entries, ht);
gncObjectForeach (GNC_CUSTOMER_MODULE_NAME, book, taxtable_scrub_cust, ht);
gncObjectForeach (GNC_VENDOR_MODULE_NAME, book, taxtable_scrub_vendor, ht);
gncObjectForeach (_GNC_MOD_NAME, book, taxtable_scrub_cb, &list);
/* destroy the list of "grandchildren" tax tables */
for (node = list; node; node = node->next) {
table = node->data;
PINFO ("deleting grandchild taxtable: %s\n",
guid_to_string(gncTaxTableGetGUID(table)));
/* Make sure the parent has no children */
parent = gncTaxTableGetParent(table);
gncTaxTableSetChild(parent, NULL);
/* Destroy this tax table */
gncTaxTableBeginEdit(table);
gncTaxTableDestroy(table);
}
/* reset the refcounts as necessary */
g_hash_table_foreach(ht, taxtable_reset_refcount, NULL);
g_list_free(list);
g_hash_table_destroy(ht);
}
void
gnc_taxtable_xml_initialize (void)
{
@ -471,6 +657,7 @@ gnc_taxtable_xml_initialize (void)
NULL, /* add_item */
taxtable_get_count,
taxtable_write,
taxtable_scrub,
};
gncObjectRegisterBackend (_GNC_MOD_NAME,

View File

@ -461,6 +461,7 @@ gnc_vendor_xml_initialize (void)
NULL, /* add_item */
vendor_get_count,
vendor_write,
NULL, /* scrub */
};
gncObjectRegisterBackend (_GNC_MOD_NAME,

View File

@ -232,6 +232,10 @@ static void gncEntryFree (GncEntry *entry)
gncAccountValueDestroy (entry->i_tax_values);
if (entry->b_tax_values)
gncAccountValueDestroy (entry->b_tax_values);
if (entry->i_tax_table)
gncTaxTableDecRef (entry->i_tax_table);
if (entry->b_tax_table)
gncTaxTableDecRef (entry->b_tax_table);
remObj (entry);
g_free (entry);

View File

@ -33,6 +33,8 @@ struct _gncTaxTable {
GncTaxTable * child; /* if non-null, we have not changed */
gboolean invisible;
GList * children; /* A list of children */
int editlevel;
gboolean do_free;
gboolean dirty;
@ -173,19 +175,54 @@ void gncTaxTableDestroy (GncTaxTable *table)
static void gncTaxTableFree (GncTaxTable *table)
{
GList *list;
GncTaxTable *child;
if (!table) return;
gnc_engine_generate_event (&table->guid, GNC_EVENT_DESTROY);
CACHE_REMOVE (table->name);
remObj (table);
/* destroy the list of entries */
for (list = table->entries; list; list=list->next)
gncTaxTableEntryDestroy (list->data);
g_list_free (table->entries);
if (!table->do_free)
PERR("free a taxtable without do_free set!");
/* disconnect from the children */
for (list = table->children; list; list=list->next) {
child = list->data;
gncTaxTableSetParent(child, NULL);
}
g_list_free(table->children);
g_free (table);
}
static void
gncTaxTableAddChild (GncTaxTable *table, GncTaxTable *child)
{
g_return_if_fail(table);
g_return_if_fail(child);
g_return_if_fail(table->do_free == FALSE);
table->children = g_list_prepend(table->children, child);
}
static void
gncTaxTableRemoveChild (GncTaxTable *table, GncTaxTable *child)
{
g_return_if_fail(table);
g_return_if_fail(child);
if (table->do_free)
return;
table->children = g_list_remove(table->children, child);
}
GncTaxTableEntry * gncTaxTableEntryCreate (void)
{
GncTaxTableEntry *entry;
@ -227,7 +264,11 @@ void gncTaxTableSetParent (GncTaxTable *table, GncTaxTable *parent)
{
if (!table) return;
gncTaxTableBeginEdit (table);
if (table->parent)
gncTaxTableRemoveChild(table->parent, table);
table->parent = parent;
if (parent)
gncTaxTableAddChild(parent, table);
table->refcount = 0;
gncTaxTableMakeInvisible (table);
gncTaxTableCommitEdit (table);
@ -244,7 +285,7 @@ void gncTaxTableSetChild (GncTaxTable *table, GncTaxTable *child)
void gncTaxTableIncRef (GncTaxTable *table)
{
if (!table) return;
if (table->parent) return; /* children dont need refcounts */
if (table->parent || table->invisible) return; /* children dont need refcounts */
gncTaxTableBeginEdit (table);
table->refcount++;
gncTaxTableCommitEdit (table);
@ -253,7 +294,7 @@ void gncTaxTableIncRef (GncTaxTable *table)
void gncTaxTableDecRef (GncTaxTable *table)
{
if (!table) return;
if (table->parent) return; /* children dont need refcounts */
if (table->parent || table->invisible) return; /* children dont need refcounts */
gncTaxTableBeginEdit (table);
table->refcount--;
g_return_if_fail (table->refcount >= 0);
@ -451,7 +492,7 @@ GncTaxTable *gncTaxTableReturnChild (GncTaxTable *table, gboolean make_new)
if (!table) return NULL;
if (table->child) return table->child;
if (table->parent) return table;
if (table->parent || table->invisible) return table;
if (make_new) {
child = gncTaxTableCopy (table);
gncTaxTableSetChild (table, child);