mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
* 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:
parent
21edb97a57
commit
d2c193825a
33
ChangeLog
33
ChangeLog
@ -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
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
|
||||
/**
|
||||
|
@ -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,
|
||||
|
@ -517,6 +517,7 @@ gnc_customer_xml_initialize (void)
|
||||
NULL, /* add_item */
|
||||
customer_get_count,
|
||||
customer_write,
|
||||
NULL, /* scrub */
|
||||
};
|
||||
|
||||
gncObjectRegisterBackend (_GNC_MOD_NAME,
|
||||
|
@ -431,6 +431,7 @@ gnc_employee_xml_initialize (void)
|
||||
NULL, /* add_item */
|
||||
employee_get_count,
|
||||
employee_write,
|
||||
NULL, /* scrub */
|
||||
};
|
||||
|
||||
gncObjectRegisterBackend (_GNC_MOD_NAME,
|
||||
|
@ -825,6 +825,7 @@ gnc_entry_xml_initialize (void)
|
||||
NULL, /* add_item */
|
||||
entry_get_count,
|
||||
entry_write,
|
||||
NULL, /* scrub */
|
||||
};
|
||||
|
||||
gncObjectRegisterBackend (_GNC_MOD_NAME,
|
||||
|
@ -551,6 +551,7 @@ gnc_invoice_xml_initialize (void)
|
||||
NULL, /* add_item */
|
||||
invoice_get_count,
|
||||
invoice_write,
|
||||
NULL, /* scrub */
|
||||
};
|
||||
|
||||
gncObjectRegisterBackend (_GNC_MOD_NAME,
|
||||
|
@ -336,6 +336,7 @@ gnc_job_xml_initialize (void)
|
||||
NULL, /* add_item */
|
||||
job_get_count,
|
||||
job_write,
|
||||
NULL, /* scrub */
|
||||
};
|
||||
|
||||
gncObjectRegisterBackend (_GNC_MOD_NAME,
|
||||
|
@ -374,6 +374,7 @@ gnc_order_xml_initialize (void)
|
||||
NULL, /* add_item */
|
||||
order_get_count,
|
||||
order_write,
|
||||
NULL, /* scrub */
|
||||
};
|
||||
|
||||
gncObjectRegisterBackend (_GNC_MOD_NAME,
|
||||
|
@ -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,
|
||||
|
@ -461,6 +461,7 @@ gnc_vendor_xml_initialize (void)
|
||||
NULL, /* add_item */
|
||||
vendor_get_count,
|
||||
vendor_write,
|
||||
NULL, /* scrub */
|
||||
};
|
||||
|
||||
gncObjectRegisterBackend (_GNC_MOD_NAME,
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user