[Gnucash-changes] New QOF type, QOF_TYPE_COLLECT and recursive entity copying

Neil Williams codehelp at cvs.gnucash.org
Sun May 8 16:27:53 EDT 2005


Log Message:
-----------
New QOF type, QOF_TYPE_COLLECT and recursive entity copying

Tags:
----
gnucash-gnome2-dev

Modified Files:
--------------
    gnucash:
        ChangeLog
    gnucash/src/backend/file/test:
        .cvsignore
    gnucash/src/backend/qsf:
        qsf-backend.c
    gnucash/src/business/business-core:
        gncAddress.c
        gncAddress.h
        gncCustomer.c
        gncCustomer.h
        gncInvoice.c
        gncInvoice.h
        gncOwner.c
        gncOwner.h
    gnucash/src/business/business-gnome:
        gnc-plugin-business.c
    gnucash/src/engine:
        kvp_frame.h
        qof_book_merge.c
        qofclass.h
        qofid.c
        qofid.h
        qofsession.c
        qofsession.h

Revision Data
-------------
Index: ChangeLog
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/ChangeLog,v
retrieving revision 1.1487.2.202
retrieving revision 1.1487.2.203
diff -LChangeLog -LChangeLog -u -r1.1487.2.202 -r1.1487.2.203
--- ChangeLog
+++ ChangeLog
@@ -1,3 +1,28 @@
+2005-05-08  Neil Williams <linux at codehelp.co.uk>
+	* src/backend/file/test/.cvsignore: Tweak
+	* src/backend/qsf/qsf-backend.c: Handling collections
+	in QSF
+	* src/business/business-core/gncAddress.c: QOF enhancements.
+	* src/business/business-core/gncAddress.h: QOF enhancements.
+	* src/business/business-core/gncCustomer.c: Setting the address
+	via QOF.
+	* src/business/business-core/gncCustomer.h: Address handling in QOF.
+	* src/business/business-core/gncInvoice.c: Using QOF_TYPE_COLLECT for
+	owner, billto and entries.
+	* src/business/business-core/gncInvoice.h: Declarations for collect
+	handlers.
+	* src/business/business-core/gncOwner.c: Handling as a collection.
+	* src/business/business-core/gncOwner.h: Easing QOF handling.
+	* src/business/business-gnome/gnc-plugin-business.c: Recursive
+	handling.
+	* src/engine/kvp_frame.h: Doxygen fix.
+	* src/engine/qof_book_merge.c: QOF_TYPE_COLLECT merges.
+	* src/engine/qofclass.h: New QOF type, collection.
+	* src/engine/qofid.c: Collection handling.
+	* src/engine/qofid.h: Documenting QOF_TYPE_COLLECT.
+	* src/engine/qofsession.c: Recursive copies.
+	* src/engine/qofsession.h: Recursive copying documentation.
+
 2005-05-03  Neil Williams <linux at codehelp.co.uk>
 
 	* src/app-utils/gnc-ui-util.h: Doxygen update.
Index: qsf-backend.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/backend/qsf/Attic/qsf-backend.c,v
retrieving revision 1.1.2.9
retrieving revision 1.1.2.10
diff -Lsrc/backend/qsf/qsf-backend.c -Lsrc/backend/qsf/qsf-backend.c -u -r1.1.2.9 -r1.1.2.10
--- src/backend/qsf/qsf-backend.c
+++ src/backend/qsf/qsf-backend.c
@@ -79,6 +79,7 @@
 	params->supported_types = g_slist_append(params->supported_types, QOF_TYPE_DOUBLE);
 	params->supported_types = g_slist_append(params->supported_types, QOF_TYPE_CHAR);
 	params->supported_types = g_slist_append(params->supported_types, QOF_TYPE_KVP);
+	params->supported_types = g_slist_append(params->supported_types, QOF_TYPE_COLLECT);
 	qsf_time_precision = "%j";
 	qsf_time_now_t = time(NULL);
 	qsf_ts = g_new(Timespec, 1);
@@ -135,7 +136,6 @@
 	
 	qsf_be = (QSFBackend*)be;
 	g_return_if_fail(qsf_be != NULL);
-
 	qsf_free_params(qsf_be->params);
 	g_free(qsf_be->fullpath);
 	qsf_be->fullpath = NULL;
@@ -225,7 +225,6 @@
 	g_return_val_if_fail(doc != NULL, FALSE);
 	g_return_val_if_fail(params->book != NULL, FALSE);
 	g_return_val_if_fail(params->file_type == OUR_QSF_OBJ, FALSE);
-
 	qsf_root = xmlDocGetRootElement(doc);
 	qsf_ns = qsf_root->ns;
 	iter.ns = qsf_ns;
@@ -384,6 +383,23 @@
 	}
 }
 
+static void
+qsf_from_coll_cb (QofEntity *ent, gpointer user_data)
+{
+	qsf_param *params;
+	QofParam *qof_param;
+	xmlNodePtr node;
+	gchar qsf_guid[GUID_ENCODING_LENGTH + 1];
+
+	params = (qsf_param*)user_data;
+	if(!ent || !params) { return; }
+	qof_param = params->qof_param;
+	node = xmlAddChild(params->output_node, xmlNewNode(params->qsf_ns, qof_param->param_type));
+	guid_to_string_buff(qof_entity_get_guid(ent), qsf_guid);
+	xmlNodeAddContent(node, qsf_guid);
+	xmlNewProp(node, QSF_OBJECT_TYPE ,qof_param->param_name);
+}
+
 /******* reference handling ***********/
 
 static gint
@@ -454,17 +470,14 @@
 			return;
 		}
 		ref_name = g_strdup(reference->param->param_name);
-		if(guid_to_string_buff(reference->ref_guid, qsf_guid) != NULL)
-			{
 			node = xmlAddChild(object_node, xmlNewNode(ns, QOF_TYPE_GUID));
+		guid_to_string_buff(reference->ref_guid, qsf_guid);
 			xmlNodeAddContent(node, qsf_guid);
 			xmlNewProp(node, QSF_OBJECT_TYPE ,ref_name);
-			}
 		g_free(ref_name);
 	}
 }
 
-
 /*=====================================
 	Convert QofEntity to QSF XML node
 qof_param holds the parameter sequence.
@@ -481,6 +494,7 @@
 	GString    *buffer;
 	QofParam   *qof_param;
 	KvpFrame   *qsf_kvp;
+	QofCollection *qsf_coll;
 	int        param_count;
 	gboolean   own_guid;
 	const GUID *cm_guid;
@@ -519,15 +533,20 @@
 				g_list_foreach(ref, reference_list_lookup, params);
 			}
 		}
+		if(0 == safe_strcmp(qof_param->param_type, QOF_TYPE_COLLECT))
+		{
+			qsf_coll = qof_param->param_getfcn(ent, qof_param);
+			params->qof_param = qof_param;
+			params->output_node = object_node;
+			qof_collection_foreach(qsf_coll, qsf_from_coll_cb, params);
+		}
 		if(0 == safe_strcmp(qof_param->param_type, QOF_TYPE_KVP))
 		{
 			qsf_kvp = kvp_frame_copy(qof_param->param_getfcn(ent,qof_param));
-			if(kvp_frame_is_empty(qsf_kvp) == FALSE) {
 				params->qof_param = qof_param;
 				params->output_node = object_node;
 				kvp_frame_for_each_slot(qsf_kvp, qsf_from_kvp_helper, params);
 			}
-		}
 		if((qof_param->param_setfcn != NULL) && (qof_param->param_getfcn != NULL))
 		{
 			supported = g_slist_copy(params->supported_types);
@@ -892,6 +911,35 @@
 			kvp_frame_setter = (void(*)(QofEntity*, KvpFrame*))cm_setter;
 			if(kvp_frame_setter != NULL) { kvp_frame_setter(qsf_ent, cm_kvp); }
 		}
+	if(safe_strcmp(qof_type, QOF_TYPE_COLLECT) == 0) {
+		QofCollection *qsf_coll;
+		QofIdType type;
+		QofEntityReference *reference;
+		QofParam *copy_param;
+		/* retrieve the *type* of the collection, ignore any contents. */
+		qsf_coll = cm_param->param_getfcn(qsf_ent, cm_param);
+		type = qof_collection_get_type(qsf_coll);
+		cm_guid = g_new(GUID, 1);
+		if(TRUE != string_to_guid(xmlNodeGetContent(node), cm_guid))
+		{
+			qof_backend_set_error(params->be, ERR_QSF_BAD_OBJ_GUID);
+			LEAVE (" string to guid failed for %s", xmlNodeGetContent(node));
+			return;
+		}
+		// create a QofEntityReference with this type and GUID.
+		// there is only one entity each time.
+		// cm_guid contains the GUID of the reference.
+		// type is the type of the reference.
+		reference = g_new0(QofEntityReference, 1);
+		reference->type = g_strdup(qsf_ent->e_type);
+		reference->ref_guid = cm_guid;
+		reference->ent_guid = &qsf_ent->guid;
+		copy_param = g_new0(QofParam, 1);
+		copy_param->param_name = g_strdup(cm_param->param_name);
+		copy_param->param_type = g_strdup(cm_param->param_type);
+		reference->param = copy_param;
+		params->referenceList = g_list_append(params->referenceList, reference);
+	}
 	if(safe_strcmp(qof_type, QOF_TYPE_CHAR) == 0) { 
 		char_getter = (char (*)(xmlNodePtr))xmlNodeGetContent;
 		cm_char = char_getter(node);
Index: gncOwner.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/business/business-core/gncOwner.c,v
retrieving revision 1.15.4.3
retrieving revision 1.15.4.4
diff -Lsrc/business/business-core/gncOwner.c -Lsrc/business/business-core/gncOwner.c -u -r1.15.4.3 -r1.15.4.4
--- src/business/business-core/gncOwner.c
+++ src/business/business-core/gncOwner.c
@@ -23,6 +23,7 @@
 /*
  * Copyright (C) 2001, 2002 Derek Atkins
  * Copyright (C) 2003 Linas Vepstas <linas at linas.org>
+ * Copyright (c) 2005 Neil Williams <linux at codehelp.co.uk>
  * Author: Derek Atkins <warlord at MIT.EDU>
  */
 
@@ -51,7 +52,9 @@
 
 GncOwner * gncOwnerCreate (void)
 {
-  GncOwner *o = g_new0 (GncOwner, 1);
+  GncOwner *o;
+
+  o = g_new0 (GncOwner, 1);
   o->type = GNC_OWNER_NONE;
   return o;
 }
@@ -103,6 +106,61 @@
   return owner->type;
 }
 
+AS_STRING_FUNC(GncOwnerType, ENUM_OWNER_TYPE)
+FROM_STRING_FUNC(GncOwnerType, ENUM_OWNER_TYPE)
+
+void
+qofOwnerSetType(GncOwner *owner, const char* type_string)
+{
+	if (!owner) { return; }
+	owner->type = GncOwnerTypefromString(type_string);
+	qofOwnerSetOwner(owner, NULL);
+}
+
+char*
+qofOwnerGetTypeString(GncOwner *owner)
+{
+	char* t;
+
+	if(!owner) { return NULL; }
+	t = g_strdup(GncOwnerTypeasString(owner->type));
+	return t;
+}
+
+void
+qofOwnerSetOwner(GncOwner *owner, gpointer obj)
+{
+	if(!owner) { return; }
+	if(obj != NULL) { owner->qof_temp = obj; }
+	switch(owner->type)
+	{
+		case GNC_OWNER_NONE : {
+			break;
+		}
+		case GNC_OWNER_UNDEFINED : {
+			gncOwnerInitUndefined(owner, owner->qof_temp);
+			break;
+		}
+		case GNC_OWNER_CUSTOMER : {
+			gncOwnerInitCustomer(owner, (GncCustomer*)owner->qof_temp);
+			break;
+		}
+		case GNC_OWNER_JOB : {
+			gncOwnerInitJob(owner, (GncJob*)owner->qof_temp);
+			break;
+		}
+		case GNC_OWNER_VENDOR : {
+			gncOwnerInitVendor(owner, (GncVendor*)owner->qof_temp);
+			break;
+		}
+		case GNC_OWNER_EMPLOYEE : {
+			gncOwnerInitEmployee(owner, (GncEmployee*)owner->qof_temp);
+			break;
+		}
+	}
+	owner->qof_temp = NULL;
+}
+
 gpointer gncOwnerGetUndefined (const GncOwner *owner)
 {
   if (!owner) return NULL;
@@ -138,6 +196,51 @@
   return owner->owner.employee;
 }
 
+void
+qofOwnerSetUndefined (GncOwner *owner, gpointer obj)
+{
+	if ((!owner)||(!obj)) { return; }
+	qofOwnerSetOwner(owner, obj);
+}
+
+void
+qofOwnerSetCustomer (GncOwner *owner, GncCustomer *customer)
+{
+	if((!owner)||(!customer)) { return; }
+	qofOwnerSetOwner(owner, (gpointer)customer);
+}
+
+void
+qofOwnerSetJob (GncOwner *owner, GncJob *job)
+{
+	if((!owner)||(!job)) { return; }
+	qofOwnerSetOwner(owner, (gpointer)job);
+}
+
+void
+qofOwnerSetVendor( GncOwner *owner, GncVendor *vendor)
+{
+	if((!owner)||(!vendor)) { return; }
+	qofOwnerSetOwner(owner, (gpointer)vendor);
+}
+
+void
+qofOwnerSetEmployee (GncOwner *owner, GncEmployee* employee)
+{
+	if ((!owner)||(!employee)) { return; }
+	qofOwnerSetOwner(owner, (gpointer)employee);
+}
+
+void
+qofOwnerSetEndOwner (GncOwner *owner, GncOwner *parent)
+{
+	if(!owner) { return; }
+	if(owner->type == GNC_OWNER_JOB)
+	{
+		gncJobSetOwner(owner->owner.job, parent);
+	}
+}
+
 gnc_commodity * gncOwnerGetCurrency (GncOwner *owner)
 {
   if (!owner) return NULL;
@@ -398,8 +501,8 @@
 {
   static QofParam params[] = {
     { OWNER_TYPE, QOF_TYPE_INT64, (QofAccessFunc)gncOwnerGetType, NULL },
-    { OWNER_CUSTOMER, GNC_ID_CUSTOMER,
-      (QofAccessFunc)gncOwnerGetCustomer, NULL },
+    { OWNER_TYPE_STRING, QOF_TYPE_STRING, (QofAccessFunc)qofOwnerGetTypeString, NULL },
+    { OWNER_CUSTOMER, GNC_ID_CUSTOMER, (QofAccessFunc)gncOwnerGetCustomer,      NULL },
     { OWNER_JOB, GNC_ID_JOB, (QofAccessFunc)gncOwnerGetJob, NULL },
     { OWNER_VENDOR, GNC_ID_VENDOR, (QofAccessFunc)gncOwnerGetVendor, NULL },
     { OWNER_EMPLOYEE, GNC_ID_EMPLOYEE, (QofAccessFunc)gncOwnerGetEmployee, NULL },
@@ -410,7 +513,7 @@
     { NULL },
   };
 
-  qof_class_register (_GNC_MOD_NAME, (QofSortFunc)gncOwnerCompare, params);
+  qof_class_register (GNC_ID_OWNER, (QofSortFunc)gncOwnerCompare, params);
   reg_lot ();
 
   return TRUE;
Index: gncCustomer.h
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/business/business-core/gncCustomer.h,v
retrieving revision 1.13.4.6
retrieving revision 1.13.4.7
diff -Lsrc/business/business-core/gncCustomer.h -Lsrc/business/business-core/gncCustomer.h -u -r1.13.4.6 -r1.13.4.7
--- src/business/business-core/gncCustomer.h
+++ src/business/business-core/gncCustomer.h
@@ -71,16 +71,16 @@
 #define GNC_IS_CUSTOMER(obj)  (QOF_CHECK_TYPE((obj), GNC_ID_CUSTOMER))
 #define GNC_CUSTOMER(obj)     (QOF_CHECK_CAST((obj), GNC_ID_CUSTOMER, GncCustomer))
 
-/** @name Create/Destroy Functions */
-/** @{ */
+/** @name Create/Destroy Functions 
+ @{ */
 GncCustomer *gncCustomerCreate (QofBook *book);
 void gncCustomerDestroy (GncCustomer *customer);
 void gncCustomerBeginEdit (GncCustomer *customer);
 void gncCustomerCommitEdit (GncCustomer *customer);
 /** @} */
 
-/** @name Set Functions */
-/** @{ */
+/** @name Set Functions 
+ @{ */
 
 void gncCustomerSetID (GncCustomer *customer, const char *id);
 void gncCustomerSetName (GncCustomer *customer, const char *name);
@@ -100,8 +100,8 @@
 
 /** @} */
 
-/** @name Get Functions */
-/** @{ */
+/** @name Get Functions 
+ @{ */
 /** Return a pointer to the instance gncCustomer that is identified
  *  by the guid, and is residing in the book. Returns NULL if the 
  *  instance can't be found.
@@ -130,7 +130,13 @@
 GList * gncCustomerGetJoblist (GncCustomer *customer, gboolean show_all);
 /** @} */
 
+/** \name QOF additions
+ @{
+ */
+void qofCustomerSetAddr (GncCustomer *cust, QofEntity *addr_ent);
 
+void qofCustomerSetShipAddr (GncCustomer *cust, QofEntity *ship_addr_ent);
+/** @} */
 
 gboolean gncCustomerIsDirty (GncCustomer *customer);
 int gncCustomerCompare (GncCustomer *a, GncCustomer *b);
Index: gncInvoice.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/business/business-core/gncInvoice.c,v
retrieving revision 1.56.4.8
retrieving revision 1.56.4.9
diff -Lsrc/business/business-core/gncInvoice.c -Lsrc/business/business-core/gncInvoice.c -u -r1.56.4.8 -r1.56.4.9
--- src/business/business-core/gncInvoice.c
+++ src/business/business-core/gncInvoice.c
@@ -23,6 +23,7 @@
 /*
  * Copyright (C) 2001,2002 Derek Atkins
  * Copyright (C) 2003 Linas Vepstas <linas at linas.org>
+ * Copyright (c) 2005 Neil Williams <linux at codehelp.co.uk>
  * Author: Derek Atkins <warlord at MIT.EDU>
  */
 
@@ -64,17 +65,17 @@
 {
   QofInstance inst;
   
-  char *	id;
-  char *	notes;
+  char        *id;
+  char        *notes;
   gboolean 	active;
 
-  char *	billing_id;
-  char *	printname;
-  GncBillTerm *	terms;
-  GList * 	entries;
+  char        *billing_id;
+  char        *printname;
+  GncBillTerm *terms;
+  GList       *entries;
   GncOwner	owner;
   GncOwner	billto;
-  GncJob *	job;
+  GncJob      *job;
   Timespec 	date_opened;
   Timespec 	date_posted;
 
@@ -82,9 +83,9 @@
 
   gnc_commodity * currency;
 
-  Account * 	posted_acc;
-  Transaction * posted_txn;
-  GNCLot *	posted_lot;
+  Account     *posted_acc;
+  Transaction *posted_txn;
+  GNCLot      *posted_lot;
 };
 
 static short	module = MOD_BUSINESS;
@@ -625,6 +626,61 @@
   return invoice->entries;
 }
 
+/* test to see how to get the list of entries.  */
+KvpFrame*
+qofInvoiceGetEntries (GncInvoice *invoice)
+{
+	KvpFrame   *invoice_frame;
+	KvpValue   *entry_value;
+	GList      *entry_list, *i;
+	QofEntity  *entry;
+	const GUID *entry_guid;
+	const char *entry_path;
+
+	entry_list = NULL;
+	entry_value = NULL;
+	entry_path = g_strdup("Invoice/Entry");
+	invoice_frame = kvp_frame_new();
+	entry_list = gncInvoiceGetEntries(invoice);
+	for(i = entry_list; i != NULL; i = i->next)
+	{
+		entry = (QofEntity*)entry_list->data;
+		entry_guid = qof_entity_get_guid(entry);
+		kvp_frame_add_guid(invoice_frame, entry_path, entry_guid);
+	}
+	return invoice_frame;
+}
+
+void 
+qofInvoiceSetEntries(GncInvoice *invoice, KvpFrame *invoice_frame)
+{
+	GList *entry_list;
+	KvpValue *entry_value;
+	const char *entry_path;
+
+	entry_path = g_strdup("Invoice/Entry");
+	entry_value = kvp_frame_get_slot_path(invoice_frame, entry_path);
+	for(entry_list = kvp_value_get_glist(entry_value); entry_list != NULL; entry_list = entry_list->next)
+	{
+		gncInvoiceAddEntry(invoice, (GncEntry*)entry_list->data);
+	}
+	kvp_frame_delete(invoice_frame);
+}
+
+GncJob*
+qofInvoiceGetJob (GncInvoice *invoice)
+{
+	if(!invoice) { return NULL; }
+	return invoice->job;
+}
+
+void 
+qofInvoiceSetJob (GncInvoice *invoice, GncJob *job)
+{
+	if(!invoice) { return; }
+	invoice->job = job;
+}
+
 static void
 gncInvoiceDetachFromLot (GNCLot *lot)
 {
@@ -1426,7 +1482,7 @@
 {
   static QofParam params[] = {
     { INVOICE_ID, QOF_TYPE_STRING, (QofAccessFunc)gncInvoiceGetID, (QofSetterFunc)gncInvoiceSetID },
-    { INVOICE_OWNER, GNC_ID_OWNER, (QofAccessFunc)gncInvoiceGetOwner, (QofSetterFunc)gncInvoiceSetOwner },
+    { INVOICE_OWNER,   GNC_ID_OWNER,    (QofAccessFunc)gncInvoiceGetOwner,   NULL },
     { INVOICE_OPENED, QOF_TYPE_DATE, (QofAccessFunc)gncInvoiceGetDateOpened, (QofSetterFunc)gncInvoiceSetDateOpened },
     { INVOICE_DUE, QOF_TYPE_DATE, (QofAccessFunc)gncInvoiceGetDateDue, NULL },
     { INVOICE_POSTED, QOF_TYPE_DATE, (QofAccessFunc)gncInvoiceGetDatePosted, (QofSetterFunc)gncInvoiceSetDatePosted },
@@ -1434,12 +1490,14 @@
     { INVOICE_IS_PAID, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncInvoiceIsPaid, NULL },
     { INVOICE_BILLINGID, QOF_TYPE_STRING, (QofAccessFunc)gncInvoiceGetBillingID, (QofSetterFunc)gncInvoiceSetBillingID },
     { INVOICE_NOTES, QOF_TYPE_STRING, (QofAccessFunc)gncInvoiceGetNotes, (QofSetterFunc)gncInvoiceSetNotes },
-    { INVOICE_ACC, GNC_ID_ACCOUNT, (QofAccessFunc)gncInvoiceGetPostedAcc, NULL },
-    { INVOICE_POST_TXN, GNC_ID_TRANS, (QofAccessFunc)gncInvoiceGetPostedTxn, NULL },
-    { INVOICE_POST_LOT, GNC_ID_LOT, (QofAccessFunc)gncInvoiceGetPostedLot, NULL },
+    { INVOICE_ACC,     GNC_ID_ACCOUNT,  (QofAccessFunc)gncInvoiceGetPostedAcc, (QofSetterFunc)gncInvoiceSetPostedAcc },
+    { INVOICE_POST_TXN, GNC_ID_TRANS,   (QofAccessFunc)gncInvoiceGetPostedTxn, (QofSetterFunc)gncInvoiceSetPostedTxn },
+    { INVOICE_POST_LOT, GNC_ID_LOT,     (QofAccessFunc)gncInvoiceGetPostedLot, NULL/*(QofSetterFunc)gncInvoiceSetPostedLot*/ },
     { INVOICE_TYPE, QOF_TYPE_STRING, (QofAccessFunc)gncInvoiceGetType, NULL },
     { INVOICE_TERMS, GNC_ID_BILLTERM, (QofAccessFunc)gncInvoiceGetTerms, (QofSetterFunc)gncInvoiceSetTerms },
-    { INVOICE_BILLTO, GNC_ID_OWNER, (QofAccessFunc)gncInvoiceGetBillTo, (QofSetterFunc)gncInvoiceSetBillTo },
+    { INVOICE_BILLTO,  GNC_ID_OWNER,    (QofAccessFunc)gncInvoiceGetBillTo,  NULL },
+    { INVOICE_ENTRIES, QOF_TYPE_KVP,    (QofAccessFunc)qofInvoiceGetEntries, (QofSetterFunc)qofInvoiceSetEntries },
+    { INVOICE_JOB,     GNC_ID_JOB,      (QofAccessFunc)qofInvoiceGetJob,     (QofSetterFunc)qofInvoiceSetJob }, 
     { QOF_PARAM_ACTIVE, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncInvoiceGetActive, (QofSetterFunc)gncInvoiceSetActive },
     { QOF_PARAM_BOOK, QOF_ID_BOOK, (QofAccessFunc)qof_instance_get_book, NULL },
     { QOF_PARAM_GUID, QOF_TYPE_GUID, (QofAccessFunc)qof_instance_get_guid, NULL },
Index: gncAddress.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/business/business-core/gncAddress.c,v
retrieving revision 1.4.4.4
retrieving revision 1.4.4.5
diff -Lsrc/business/business-core/gncAddress.c -Lsrc/business/business-core/gncAddress.c -u -r1.4.4.4 -r1.4.4.5
--- src/business/business-core/gncAddress.c
+++ src/business/business-core/gncAddress.c
@@ -32,16 +32,19 @@
 #include "gnc-engine-util.h"	/* safe_strcmp */
 #include "qofquerycore.h"
 #include "qofclass.h"
+#include "qofinstance-p.h"
 #include "guid.h"
 #include "gnc-event-p.h"
-
+#include "qofquery.h"
 #include "gncAddress.h"
 #include "gncAddressP.h"
+#include "gncOwner.h"
 
 struct _gncAddress 
 {
   QofBook *	book;
   QofEntity * parent;
+  QofInstance inst;
   gboolean	dirty;
   char *	name;
   char *	addr1;
@@ -77,6 +80,7 @@
   if (!book) return NULL;
 
   addr = g_new0 (GncAddress, 1);
+  qof_instance_init(&addr->inst, GNC_ID_ADDRESS, book);
   addr->book = book;
   addr->dirty = FALSE;
   addr->parent = prnt;
@@ -94,6 +98,45 @@
 }
 
 GncAddress * 
+qofAddressCreate (QofBook *book)
+{
+  GncAddress *addr;
+
+  if (!book) return NULL;
+
+  addr = g_new0 (GncAddress, 1);
+  qof_instance_init(&addr->inst, GNC_ID_ADDRESS, book);
+  addr->book = book;
+  addr->dirty = FALSE;
+  addr->parent = NULL; /* set using an independent function. */
+
+  addr->name = CACHE_INSERT ("");
+  addr->addr1 = CACHE_INSERT ("");
+  addr->addr2 = CACHE_INSERT ("");
+  addr->addr3 = CACHE_INSERT ("");
+  addr->addr4 = CACHE_INSERT ("");
+  addr->phone = CACHE_INSERT ("");
+  addr->fax = CACHE_INSERT ("");
+  addr->email = CACHE_INSERT ("");
+
+  return addr;
+}
+
+void
+qofAddressSetOwner(GncAddress *addr, QofEntity *owner)
+{
+	if(!addr) { return; }
+	addr->parent = owner;
+}
+
+QofEntity*
+qofAddressGetOwner(GncAddress *addr)
+{
+	if(!addr) { return NULL; }
+	return addr->parent;
+}
+
+GncAddress * 
 gncCloneAddress (GncAddress *from, QofEntity *new_parent, QofBook *book)
 {
   GncAddress *addr;
@@ -281,6 +324,21 @@
   return safe_strcmp (a->name, b->name);
 }
 
+static QofObject GncAddressDesc =
+{
+	interface_version:  QOF_OBJECT_VERSION,
+	e_type:             GNC_ID_ADDRESS,
+	type_label:         "Address",
+	create:             (gpointer)qofAddressCreate,
+	book_begin:         NULL,
+	book_end:           NULL,
+	is_dirty:           NULL,
+	mark_clean:         NULL,
+	foreach:            qof_collection_foreach,
+	printable:          NULL,
+	version_cmp:        (int (*)(gpointer, gpointer)) qof_instance_version_cmp,
+};
+
 gboolean gncAddressRegister (void)
 {
   static QofParam params[] = {
@@ -293,10 +351,13 @@
     { ADDRESS_PHONE, 	QOF_TYPE_STRING, (QofAccessFunc)gncAddressGetPhone, (QofSetterFunc)gncAddressSetPhone },
     { ADDRESS_FAX, 		QOF_TYPE_STRING, (QofAccessFunc)gncAddressGetFax, 	(QofSetterFunc)gncAddressSetFax },
     { ADDRESS_EMAIL, 	QOF_TYPE_STRING, (QofAccessFunc)gncAddressGetEmail, (QofSetterFunc)gncAddressSetEmail },
+    { ADDRESS_OWNER, GNC_ID_OWNER,    (QofAccessFunc)qofAddressGetOwner, (QofSetterFunc)qofAddressSetOwner },
+    { QOF_PARAM_BOOK, QOF_ID_BOOK,   (QofAccessFunc)qof_instance_get_book, NULL },
+    { QOF_PARAM_GUID, QOF_TYPE_GUID, (QofAccessFunc)qof_instance_get_guid, NULL },
     { NULL },
   };
 
-  qof_class_register (_GNC_MOD_NAME, (QofSortFunc)gncAddressCompare, params);
+  qof_class_register (GNC_ID_ADDRESS, (QofSortFunc)gncAddressCompare, params);
 
-  return TRUE;
+  return qof_object_register(&GncAddressDesc);
 }
Index: gncCustomer.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/business/business-core/gncCustomer.c,v
retrieving revision 1.20.4.5
retrieving revision 1.20.4.6
diff -Lsrc/business/business-core/gncCustomer.c -Lsrc/business/business-core/gncCustomer.c -u -r1.20.4.5 -r1.20.4.6
--- src/business/business-core/gncCustomer.c
+++ src/business/business-core/gncCustomer.c
@@ -426,6 +426,26 @@
   return cust->addr;
 }
 
+void
+qofCustomerSetAddr (GncCustomer *cust, QofEntity *addr_ent)
+{
+	GncAddress *addr;
+
+	if(!cust) { return; }
+	addr = (GncAddress*)addr_ent;
+	cust->addr = addr;
+}
+
+void
+qofCustomerSetShipAddr (GncCustomer *cust, QofEntity *ship_addr_ent)
+{
+	GncAddress *ship_addr;
+
+	if(!cust) { return; }
+	ship_addr = (GncAddress*)ship_addr_ent;
+	cust->shipaddr = ship_addr;
+}
+
 GncAddress * gncCustomerGetShipAddr (GncCustomer *cust)
 {
   if (!cust) return NULL;
@@ -556,8 +576,8 @@
 		(QofSetterFunc)gncCustomerSetDiscount },
 	{ CUSTOMER_CREDIT, QOF_TYPE_NUMERIC, (QofAccessFunc)gncCustomerGetCredit,
 		(QofSetterFunc)gncCustomerSetCredit },
-    { CUSTOMER_ADDR, GNC_ADDRESS_MODULE_NAME, (QofAccessFunc)gncCustomerGetAddr, NULL },
-    { CUSTOMER_SHIPADDR, GNC_ADDRESS_MODULE_NAME, (QofAccessFunc)gncCustomerGetShipAddr, NULL },
+    { CUSTOMER_ADDR, GNC_ID_ADDRESS, (QofAccessFunc)gncCustomerGetAddr, (QofSetterFunc)qofCustomerSetAddr },
+    { CUSTOMER_SHIPADDR, GNC_ID_ADDRESS, (QofAccessFunc)gncCustomerGetShipAddr, (QofSetterFunc)qofCustomerSetShipAddr },
 	{ CUSTOMER_TT_OVER, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncCustomerGetTaxTableOverride, 
 		(QofSetterFunc)gncCustomerSetTaxTableOverride },
     { QOF_PARAM_ACTIVE, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncCustomerGetActive, (QofSetterFunc)gncCustomerSetActive },
Index: gncAddress.h
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/business/business-core/gncAddress.h,v
retrieving revision 1.4.4.4
retrieving revision 1.4.4.5
diff -Lsrc/business/business-core/gncAddress.h -Lsrc/business/business-core/gncAddress.h -u -r1.4.4.4 -r1.4.4.5
--- src/business/business-core/gncAddress.h
+++ src/business/business-core/gncAddress.h
@@ -22,10 +22,33 @@
 /** @addtogroup Business
     @{ */
 /** @addtogroup Address
+
+An address belongs to another object, determined by the ::GncOwner.
+It is the owner that assigns a name and identifier to the address.
+In effect, an address is just a building - to make it useful to
+GnuCash, it needs to be tied to a person. After all, you cannot
+invoice a building, you invoice a person working / living in the
+building.
+
+QOF needs to handle all objects generically and to tie the address
+to an owner, QOF must be able to find each - as entities.
+
+This allows QOF to follow the hierarchy of objects without having
+to call any application-specific routines.
+
+To achieve this, new GncAddress routines have been added. An address
+is now created with a NULL parent and the parent set explicitly using
+the QOF object declaration. Whilst this adds functionality, it is
+important that a valid ::GncOwner entity is always set as a parent.
+This is an API issue - QOF will always set the parent provided that
+a suitable entity is passed to the qofAddressSetOwner routine. It is
+up to you to pass a suitable entity.
+
     @{ */
 /** @file gncAddress.h
     @brief an Address object
     @author Copyright (C) 2001 Derek Atkins <warlord at MIT.EDU>
+    @author Copyright (c) 2005 Neil Williams <linux at codehelp.co.uk>
 */
 
 #ifndef GNC_ADDRESS_H_
@@ -38,7 +61,7 @@
 #include "qofid-p.h"
 
 #define GNC_ADDRESS_MODULE_NAME        "gncAddress"
-
+#define GNC_ID_ADDRESS GNC_ADDRESS_MODULE_NAME
 /** \struct GncAddress
 
 @param	QofBook *	book;
@@ -55,17 +78,26 @@
 */
 typedef struct _gncAddress GncAddress;
 
-/** @name Create/Destroy functions */
-/** @{ */
+/** @name Create/Destroy functions 
+ @{ */
 /** create a new address */
 GncAddress * gncAddressCreate (QofBook *book, QofEntity *parent);
+/** \brief QOF address creation 
+
+An address cannot exist without a parent, yet to merge and 
+export the parent, a QOF address object must exist and it 
+must be created using standard QOF calls. QOF will always 
+set the parent.
+*/
+GncAddress* qofAddressCreate (QofBook *book);
+
 /** destroy an address */
 void gncAddressDestroy (GncAddress *addr);
 
 /** @} */
 
-/** @name Set functions */
-/** @{ */
+/** @name Set functions 
+ @{ */
 
 void gncAddressSetName (GncAddress *addr, const char *name);
 void gncAddressSetAddr1 (GncAddress *addr, const char *addr1);
@@ -76,10 +108,19 @@
 void gncAddressSetFax (GncAddress *addr, const char *fax);
 void gncAddressSetEmail (GncAddress *addr, const char *email);
 void gncAddressClearDirty (GncAddress *address);
+/** \brief Set the address owner.
+
+\note Every address must have a genuine ::GncOwner as this 
+provides the name or identifier to use the address.
+
+In order to export a Customer, QOF must be able to find the
+address as an entity.
+*/
+void qofAddressSetOwner (GncAddress *address, QofEntity *owner);
 /** @} */
 
-/** @name Get Functions */
-/** @{ */
+/** @name Get Functions 
+ @{ */
 
 const char * gncAddressGetName (const GncAddress *addr);
 const char * gncAddressGetAddr1 (const GncAddress *addr);
@@ -89,6 +130,7 @@
 const char * gncAddressGetPhone (const GncAddress *addr);
 const char * gncAddressGetFax (const GncAddress *addr);
 const char * gncAddressGetEmail (const GncAddress *addr);
+QofEntity*   qofAddressGetOwner (GncAddress *addr);
 /** @} */
 
 gboolean gncAddressIsDirty (const GncAddress *addr);
@@ -108,6 +150,7 @@
 #define ADDRESS_PHONE   "phone"
 #define ADDRESS_FAX     "fax"
 #define ADDRESS_EMAIL   "email"
+#define ADDRESS_OWNER   "owner"
 
 #endif /* GNC_ADDRESS_H_ */
 /** @} */
Index: gncInvoice.h
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/business/business-core/gncInvoice.h,v
retrieving revision 1.29.4.5
retrieving revision 1.29.4.6
diff -Lsrc/business/business-core/gncInvoice.h -Lsrc/business/business-core/gncInvoice.h -u -r1.29.4.5 -r1.29.4.6
--- src/business/business-core/gncInvoice.h
+++ src/business/business-core/gncInvoice.h
@@ -22,10 +22,15 @@
 /** @addtogroup Business
     @{ */
 /** @addtogroup Invoice
+
+An invoice holds a list of entries, a pointer to the customer, 
+and the job, the dates entered and posted, as well as the account, 
+transaction and lot for the posted invoice.
     @{ */
 /** @file gncInvoice.h
     @brief  Business Invoice Interface 
     @author Copyright (C) 2001 Derek Atkins <warlord at MIT.EDU>
+    @author Copyright (c) 2005 Neil Williams <linux at codehelp.co.uk>
 */
 
 #ifndef GNC_INVOICE_H_
@@ -46,14 +51,14 @@
 #define GNC_IS_INVOICE(obj)  (QOF_CHECK_TYPE((obj), GNC_ID_INVOICE))
 #define GNC_INVOICE(obj)     (QOF_CHECK_CAST((obj), GNC_ID_INVOICE, GncInvoice))
 
-/** @name Create/Destroy Functions */
-/** @{ */
+/** @name Create/Destroy Functions 
+ @{ */
 GncInvoice *gncInvoiceCreate (QofBook *book);
 void gncInvoiceDestroy (GncInvoice *invoice);
 /** @} */
 
-/** @name Set Functions */
-/** @{ */
+/** @name Set Functions 
+ @{ */
 void gncInvoiceSetID (GncInvoice *invoice, const char *id);
 void gncInvoiceSetOwner (GncInvoice *invoice, GncOwner *owner);
 void gncInvoiceSetDateOpened (GncInvoice *invoice, Timespec date);
@@ -65,6 +70,18 @@
 void gncInvoiceSetActive (GncInvoice *invoice, gboolean active);
 void gncInvoiceSetBillTo (GncInvoice *invoice, GncOwner *billto);
 void gncInvoiceSetToChargeAmount (GncInvoice *invoice, gnc_numeric amount);
+void qofInvoiceSetOwner (GncInvoice *invoice, QofEntity *ent);
+/** \brief create the entry list from a temporary frame.
+
+An invoice can hold an open ended list of entries that are summed to
+make the total payable. To represent these entries within the invoice,
+KVP is used to list the references to the GncEntry's for the invoice.
+
+The KvpFrame of KVP_TYPE_GLIST KvpValues is converted to the internal
+GList of entries for this invoice.
+*/
+void qofInvoiceSetEntries(GncInvoice *invoice, KvpFrame *invoice_frame);
+void qofInvoiceSetJob (GncInvoice *invoice, GncJob *job);
 /** @} */
 
 void gncInvoiceAddEntry (GncInvoice *invoice, GncEntry *entry);
@@ -74,8 +91,8 @@
 void gncBillAddEntry (GncInvoice *bill, GncEntry *entry);
 void gncBillRemoveEntry (GncInvoice *bill, GncEntry *entry);
 
-/** @name Get Functions */
-/** @{ */
+/** @name Get Functions 
+ @{ */
 const char * gncInvoiceGetID (GncInvoice *invoice);
 GncOwner * gncInvoiceGetOwner (GncInvoice *invoice);
 Timespec gncInvoiceGetDateOpened (GncInvoice *invoice);
@@ -89,6 +106,12 @@
 GncOwner * gncInvoiceGetBillTo (GncInvoice *invoice);
 gnc_numeric gncInvoiceGetToChargeAmount (GncInvoice *invoice);
 gboolean gncInvoiceGetActive (GncInvoice *invoice);
+/** \brief Create a temporary frame for the entry list.
+
+Converts the GList of GncEntry inside GncInvoice to a KVP_TYPE_GLIST.
+*/
+KvpFrame* qofInvoiceGetEntries (GncInvoice *invoice);
+GncJob* qofInvoiceGetJob (GncInvoice *invoice);
 
 GNCLot * gncInvoiceGetPostedLot (GncInvoice *invoice);
 Transaction * gncInvoiceGetPostedTxn (GncInvoice *invoice);
@@ -176,6 +199,8 @@
 #define INVOICE_POST_LOT	"posted_lot"
 #define INVOICE_TYPE	"type"
 #define INVOICE_BILLTO	"bill-to"
+#define INVOICE_ENTRIES     "list_of_entries"
+#define INVOICE_JOB         "invoice_job"
 
 #define INVOICE_FROM_LOT	"invoice-from-lot"
 #define INVOICE_FROM_TXN	"invoice-from-txn"
Index: gncOwner.h
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/business/business-core/gncOwner.h,v
retrieving revision 1.12.4.3
retrieving revision 1.12.4.4
diff -Lsrc/business/business-core/gncOwner.h -Lsrc/business/business-core/gncOwner.h -u -r1.12.4.3 -r1.12.4.4
--- src/business/business-core/gncOwner.h
+++ src/business/business-core/gncOwner.h
@@ -26,6 +26,7 @@
 /** @file gncOwner.h
     @brief Business Interface:  Object OWNERs
     @author Copyright (C) 2001,2002 Derek Atkins <warlord at MIT.EDU>
+    @author Copyright (c) 2005 Neil Williams <linux at codehelp.co.uk>
 */
 
 #ifndef GNC_OWNER_H_
@@ -35,45 +36,96 @@
 
 #define GNC_ID_OWNER   "gncOwner"
 
+#include "qofinstance-p.h"
 #include "gncCustomer.h"
 #include "gncJob.h"
 #include "gncVendor.h"
 #include "gncEmployee.h"
 #include "gnc-lot.h" 
+#include "gnc-engine-util.h"
 
-typedef enum {
-  GNC_OWNER_NONE,
-  GNC_OWNER_UNDEFINED,
-  GNC_OWNER_CUSTOMER,
-  GNC_OWNER_JOB,
-  GNC_OWNER_VENDOR,
-  GNC_OWNER_EMPLOYEE
-} GncOwnerType;
+#define ENUM_OWNER_TYPE(_) \
+  _(GNC_OWNER_NONE,)       \
+  _(GNC_OWNER_UNDEFINED,)  \
+  _(GNC_OWNER_CUSTOMER,)   \
+  _(GNC_OWNER_JOB,)        \
+  _(GNC_OWNER_VENDOR,)     \
+  _(GNC_OWNER_EMPLOYEE,)
+
+DEFINE_ENUM(GncOwnerType, ENUM_OWNER_TYPE)
+
+/** \name QOF handling
+
+Whilst GncOwner is not a formal QOF object, these functions
+are still expected to be useful in making GncOwner transparent
+to QOF as they can be used by objects like GncInvoice.
+@{
+*/
+/** \brief Allow the type to be set separate from the union. */
+void qofOwnerSetType(GncOwner *owner, const char* type_string);
+/** \brief Allow the union to be set independently of the type. */
+char* qofOwnerGetTypeString(GncOwner *owner);
+
+/** \brief QOF union set routine.
+
+ If ::qofOwnerSetType has already been called, initialise the owner.\n
+ If no type has been set, store the object in a temporary pointer.
+ When qofOwnerSetType is then called, qofOwnerSetOwner is called again
+ and uses the value in the pointer to initialise the owner.
+
+QOF makes sure both owner and type set functions are called once.
+*/
+void qofOwnerSetOwner(GncOwner *owner, gpointer obj);
+
+void qofOwnerSetUndefined (GncOwner *owner, gpointer obj);
+void qofOwnerSetCustomer (GncOwner *owner, GncCustomer* customer);
+void qofOwnerSetJob (GncOwner *owner, GncJob* job);
+void qofOwnerSetVendor (GncOwner *owner, GncVendor* vendor);
+void qofOwnerSetEmployee (GncOwner *owner, GncEmployee* employee);
+/** \brief Set the parent owner. */
+void qofOwnerSetEndOwner (GncOwner *owner, GncOwner *parent);
+GncOwner* qofOwnerCreate (QofBook *book);
+
+AS_STRING_DEC(GncOwnerType, ENUM_OWNER_TYPE)
+FROM_STRING_DEC(GncOwnerType, ENUM_OWNER_TYPE)
+
+gboolean
+gncOwnerRegister(void);
+
+/** @} */
 
+/** \struct GncOwner */
 struct gnc_owner_s {
-  GncOwnerType     type;
+  GncOwnerType     type;      /**< Customer, Job, Vendor, Employee or Undefined. */
   union {
     gpointer       undefined;
     GncCustomer *  customer;
     GncJob *       job;
     GncVendor *    vendor;
     GncEmployee *  employee;
-  } owner;
+  } owner;                   /**< holds the pointer to the owner object. */
+  gpointer         qof_temp; /**< Set type independently of the owner. */
 };
 
+/** \name Setup routines
+@{
+*/
 void gncOwnerInitUndefined (GncOwner *owner, gpointer obj);
 void gncOwnerInitCustomer (GncOwner *owner, GncCustomer *customer);
 void gncOwnerInitJob (GncOwner *owner, GncJob *job);
 void gncOwnerInitVendor (GncOwner *owner, GncVendor *vendor);
 void gncOwnerInitEmployee (GncOwner *owner, GncEmployee *employee);
-
+/** @} */
+/** \name Get routines.
+@{
+*/
 GncOwnerType gncOwnerGetType (const GncOwner *owner);
 gpointer gncOwnerGetUndefined (const GncOwner *owner);
 GncCustomer * gncOwnerGetCustomer (const GncOwner *owner);
 GncJob * gncOwnerGetJob (const GncOwner *owner);
 GncVendor * gncOwnerGetVendor (const GncOwner *owner);
 GncEmployee * gncOwnerGetEmployee (const GncOwner *owner);
-
+/** @} */
 void gncOwnerCopy (const GncOwner *src, GncOwner *dest);
 gboolean gncOwnerEqual (const GncOwner *a, const GncOwner *b);
 int gncOwnerCompare (const GncOwner *a, const GncOwner *b);
@@ -103,6 +155,7 @@
 gboolean gncOwnerGetOwnerFromLot (GNCLot *lot, GncOwner *owner);
 
 #define OWNER_TYPE        "type"
+#define OWNER_TYPE_STRING "type-string"  /**< Allows the type to be handled externally. */
 #define OWNER_CUSTOMER    "customer"
 #define OWNER_JOB         "job"
 #define OWNER_VENDOR      "vendor"
Index: .cvsignore
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/backend/file/test/.cvsignore,v
retrieving revision 1.5
retrieving revision 1.5.6.1
diff -Lsrc/backend/file/test/.cvsignore -Lsrc/backend/file/test/.cvsignore -u -r1.5 -r1.5.6.1
--- src/backend/file/test/.cvsignore
+++ src/backend/file/test/.cvsignore
@@ -16,3 +16,4 @@
 Makefile.in
 *.la
 *.lo
+test_file_*
Index: gnc-plugin-business.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/business/business-gnome/Attic/gnc-plugin-business.c,v
retrieving revision 1.1.2.10
retrieving revision 1.1.2.11
diff -Lsrc/business/business-gnome/gnc-plugin-business.c -Lsrc/business/business-gnome/gnc-plugin-business.c -u -r1.1.2.10 -r1.1.2.11
--- src/business/business-gnome/gnc-plugin-business.c
+++ src/business/business-gnome/gnc-plugin-business.c
@@ -666,7 +666,10 @@
 	{
 		qof_session_begin(chart_session, filename, TRUE, TRUE);
 		coll = qof_book_get_collection(book, GNC_ID_INVOICE);
-		success = qof_entity_copy_coll(chart_session, coll);
+		success = qof_entity_copy_coll_r(chart_session, coll);
+		/* Need to get the GList of GncEntry's - KVP */
+		coll = qof_book_get_collection(book, GNC_ID_CUSTOMER);
+		success = qof_entity_copy_coll_r(chart_session, coll);
 		if(success) 
 		{ 
 			qof_session_save(chart_session, NULL);
@@ -696,7 +699,7 @@
 	{
 		qof_session_begin(chart_session, filename, TRUE, TRUE);
 		coll = qof_book_get_collection(book, GNC_ID_CUSTOMER);
-		success = qof_entity_copy_coll(chart_session, coll);
+		success = qof_entity_copy_coll_r(chart_session, coll);
 		if(success) 
 		{ 
 			qof_session_save(chart_session, NULL);
@@ -726,7 +729,7 @@
 	{
 		qof_session_begin(chart_session, filename, TRUE, TRUE);
 		coll = qof_book_get_collection(book, GNC_ID_VENDOR);
-		success = qof_entity_copy_coll(chart_session, coll);
+		success = qof_entity_copy_coll_r(chart_session, coll);
 		if(success) 
 		{ 
 			qof_session_save(chart_session, NULL);
@@ -756,7 +759,7 @@
 	{
 		qof_session_begin(chart_session, filename, TRUE, TRUE);
 		coll = qof_book_get_collection(book, GNC_ID_EMPLOYEE);
-		success = qof_entity_copy_coll(chart_session, coll);
+		success = qof_entity_copy_coll_r(chart_session, coll);
 		if(success) 
 		{ 
 			qof_session_save(chart_session, NULL);
@@ -877,4 +880,3 @@
 	// Launch the invoice editor
 	gnc_ui_invoice_edit(invoice);
 }
-
Index: qof_book_merge.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/qof_book_merge.c,v
retrieving revision 1.2.2.7
retrieving revision 1.2.2.8
diff -Lsrc/engine/qof_book_merge.c -Lsrc/engine/qof_book_merge.c -u -r1.2.2.7 -r1.2.2.8
--- src/engine/qof_book_merge.c
+++ src/engine/qof_book_merge.c
@@ -350,7 +350,7 @@
 qof_book_mergeCompare( qof_book_mergeData *mergeData ) 
 {
 	qof_book_mergeRule *currentRule;
-
+	QofCollection *mergeColl, *targetColl;
 	gchar 			*stringImport, *stringTarget, *charImport, *charTarget;
 	QofEntity	 	*mergeEnt, *targetEnt, *referenceEnt;
 	const GUID 		*guidImport, *guidTarget;
@@ -476,6 +476,13 @@
 		}
 		/* No object should have QofSetterFunc defined for the book, but just to be safe, do nothing. */
 		if(safe_strcmp(mergeType, QOF_ID_BOOK) == 0) { knowntype= TRUE;	}
+		if(safe_strcmp(mergeType, QOF_TYPE_COLLECT) == 0) {
+			mergeColl = qtparam->param_getfcn(mergeEnt, qtparam);
+			targetColl = qtparam->param_getfcn(targetEnt, qtparam);
+			if(0 == qof_collection_compare(mergeColl, targetColl)) { mergeMatch = TRUE; }
+			currentRule = qof_book_mergeUpdateRule(currentRule, mergeMatch, DEFAULT_MERGE_WEIGHT);
+			knowntype = TRUE;
+		}
 		/* deal with custom type parameters : 
 		 using references to other registered QOF objects. */
 		if(knowntype == FALSE) {
@@ -817,6 +824,7 @@
 	QofEntity   *referenceEnt;
 	GSList      *linkage;
 	/* cm_ prefix used for variables that hold the data to commit */
+	QofCollection *cm_coll;
 	QofParam    *cm_param;
 	gchar       *cm_string;
 	const GUID  *cm_guid;
@@ -841,6 +849,7 @@
 	void (*char_setter)      (QofEntity*, char);
 	void (*kvp_frame_setter) (QofEntity*, KvpFrame*);
 	void (*reference_setter) (QofEntity*, QofEntity*);
+	void (*collection_setter)(QofEntity*, QofCollection*);
 
 	g_return_if_fail(rule != NULL);
 	g_return_if_fail(mergeData != NULL);
@@ -932,6 +941,12 @@
 			if(char_setter != NULL) { char_setter(rule->targetEnt, cm_char); }
 			registered_type = TRUE;
 		}
+		if(safe_strcmp(rule->mergeType, QOF_TYPE_COLLECT) == 0) {
+			cm_coll = cm_param->param_getfcn(rule->importEnt, cm_param);
+			collection_setter = (void(*)(QofEntity*, QofCollection*))cm_param->param_setfcn;
+			if(collection_setter != NULL) { collection_setter(rule->targetEnt, cm_coll); }
+			registered_type = TRUE;
+		}
 		if(registered_type == FALSE) {
 			linkage = g_slist_copy(rule->linkedEntList);
 			referenceEnt = NULL;
Index: qofid.h
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/qofid.h,v
retrieving revision 1.2.6.7
retrieving revision 1.2.6.8
diff -Lsrc/engine/qofid.h -Lsrc/engine/qofid.h -u -r1.2.6.7 -r1.2.6.8
--- src/engine/qofid.h
+++ src/engine/qofid.h
@@ -58,6 +58,9 @@
     a collection that is associated with that type, then you must use
     Books.
 
+	Entities can refer to other entities as well as to the basic
+	QOF types, using the qofclass parameters.
+
  @{ */
 /** @file qofid.h
     @brief QOF entity type identification system 
@@ -65,7 +68,6 @@
     @author Copyright (C) 2003 Linas Vepstas <linas at linas.org>
 */
 
-
 #include <string.h>
 #include "guid.h"
 
@@ -147,7 +149,7 @@
 /** Release the data associated with this entity. Dont actually free
  * the memory associated with the instance. */
 void qof_entity_release (QofEntity *);
- /* @} */
+/** @} */
 
 /** Return the GUID of this entity */
 const GUID * qof_entity_get_guid (QofEntity *);
@@ -155,8 +157,7 @@
 /** @name Collections of Entities 
  @{ */
 
-/** create a new collection of entities of type
-*/
+/** create a new collection of entities of type */
 QofCollection * qof_collection_new (QofIdType type);
 
 /** destroy the collection */
@@ -186,8 +187,49 @@
 
 /** Return value of 'dirty' flag on collection */
 gboolean qof_collection_is_dirty (QofCollection *col);
-/** @} */
 
+/** \brief Add an entity to a QOF_TYPE_COLLECT.
+
+\note These are \b NOT the same as the main collections in the book.
+
+QOF_TYPE_COLLECT is a secondary collection, used to select entities
+as references of another entity. Entities can be freely added and merged
+across these secondary collections, they will not be removed from the
+original collection as they would by using ::qof_entity_insert_entity
+or ::qof_entity_remove_entity. 
+
+*/
+gboolean
+qof_collection_add_entity (QofCollection *coll, QofEntity *ent);
+
+/** \brief Merge two QOF_TYPE_COLLECT of the same type.
+
+\note \b NOT the same as the main collections in the book.
+
+QOF_TYPE_COLLECT uses a secondary collection, independent of
+those in the book. Entities will not be removed from the
+original collection as when using ::qof_entity_insert_entity
+or ::qof_entity_remove_entity.
+
+*/
+gboolean
+qof_collection_merge (QofCollection *target, QofCollection *merge);
+
+/** \brief Compare two secondary collections.
+
+Performs a deep comparision of the collections. Each QofEntity in
+each collection is looked up in the other collection, via the GUID.
+
+\return 0 if the collections are identical or both are NULL
+otherwise -1 if target is NULL or either collection contains an entity with an invalid
+GUID or if the types of the two collections do not match,
+or +1 if merge is NULL or if any entity exists in one collection but
+not in the other.
+*/
+gint
+qof_collection_compare (QofCollection *target, QofCollection *merge);
+
+/** @} */
 
 #endif /* QOF_ID_H */
 /** @} */
Index: qofid.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/qofid.c,v
retrieving revision 1.3.2.6
retrieving revision 1.3.2.7
diff -Lsrc/engine/qofid.c -Lsrc/engine/qofid.c -u -r1.3.2.6 -r1.3.2.7
--- src/engine/qofid.c
+++ src/engine/qofid.c
@@ -201,6 +201,91 @@
   ent->collection = col;
 }
 
+gboolean
+qof_collection_add_entity (QofCollection *coll, QofEntity *ent)
+{
+	QofEntity *e;
+
+	e = NULL;
+	if (!coll || !ent) { return FALSE; }
+	if (guid_equal(&ent->guid, guid_null())) { return FALSE; }
+	g_return_val_if_fail (coll->e_type == ent->e_type, FALSE);
+	e = qof_collection_lookup_entity(coll, &ent->guid);
+	if ( e != NULL ) { return FALSE; }
+	g_hash_table_insert (coll->hash_of_entities, &ent->guid, ent);
+	return TRUE;
+}
+
+static void
+collection_merge_cb (QofEntity *ent, gpointer data)
+{
+	QofCollection *target;
+
+	target = (QofCollection*)data;
+	qof_collection_add_entity(target, ent);	
+}
+
+gboolean
+qof_collection_merge (QofCollection *target, QofCollection *merge)
+{
+	if(!target || !merge) { return FALSE; }
+	g_return_val_if_fail (target->e_type == merge->e_type, FALSE);
+	qof_collection_foreach(merge, collection_merge_cb, target);
+	return TRUE;
+}
+
+static void
+collection_compare_cb (QofEntity *ent, gpointer user_data)
+{
+	QofCollection *target;
+	QofEntity *e;
+	gint value;
+
+	e = NULL;
+	target = (QofCollection*)user_data;
+	if (!target || !ent) { return; }
+	value = *(gint*)qof_collection_get_data(target);
+	if (value != 0) { return; }
+	if (guid_equal(&ent->guid, guid_null())) 
+	{
+		value = -1;
+		qof_collection_set_data(target, &value);
+		return; 
+	}
+	g_return_if_fail (target->e_type == ent->e_type);
+	e = qof_collection_lookup_entity(target, &ent->guid);
+	if ( e == NULL )
+	{
+		value = 1;
+		qof_collection_set_data(target, &value);
+		return;
+	}
+	value = 0;
+	qof_collection_set_data(target, &value);
+}
+
+gint
+qof_collection_compare (QofCollection *target, QofCollection *merge)
+{
+	gint value;
+
+	value = 0;
+	if (!target && !merge) { return 0; }
+	if (target == merge) { return 0; }
+	if (!target && merge) { return -1; }
+	if (target && !merge) { return 1; }
+	if(target->e_type != merge->e_type) { return -1; }
+	qof_collection_set_data(target, &value);
+	qof_collection_foreach(merge, collection_compare_cb, target);
+	value = *(gint*)qof_collection_get_data(target);
+	if(value == 0) {
+		qof_collection_set_data(merge, &value);
+		qof_collection_foreach(target, collection_compare_cb, merge);
+		value = *(gint*)qof_collection_get_data(merge);
+	}
+	return value;
+}
+
 QofEntity *
 qof_collection_lookup_entity (QofCollection *col, const GUID * guid)
 {
Index: qofsession.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/qofsession.c,v
retrieving revision 1.2.4.16
retrieving revision 1.2.4.17
diff -Lsrc/engine/qofsession.c -Lsrc/engine/qofsession.c -u -r1.2.4.16 -r1.2.4.17
--- src/engine/qofsession.c
+++ src/engine/qofsession.c
@@ -370,7 +370,6 @@
 	if((param->param_getfcn != NULL)&&(param->param_setfcn != NULL)) {
 			qecd->param_list = g_slist_prepend(qecd->param_list, param);
 	}
-	if(g_slist_length(qecd->param_list) == 0) { qecd->error = TRUE; }
 }
 
 QofEntityReference*
@@ -439,6 +438,8 @@
 	/* in case of any error, let the process know. */
 	if(context->error == TRUE) { return; }
 	context->error = TRUE;
+	cm_date.tv_nsec = 0;
+	cm_date.tv_sec =  0;
 	importEnt = context->from;
 	targetEnt = context->to;
 	registered_type = FALSE;
@@ -624,7 +625,7 @@
 	QofBook *book;
 	const GUID *g;
 
-	if(qof_entity_guid_match(new_session, original)) return FALSE;
+	if(qof_entity_guid_match(new_session, original)) { return FALSE; }
 	gnc_engine_suspend_events();
 	qecd.param_list = NULL;
 	book = qof_session_get_book(new_session);
@@ -637,7 +638,7 @@
 	g = qof_entity_get_guid(original);
 	qof_entity_set_guid(qecd.to, g);
 	qof_class_param_foreach(original->e_type, qof_entity_param_cb, &qecd);
-	if(qsf_check_error(&qecd)) { return FALSE; }
+	if(g_slist_length(qecd.param_list) == 0) { return FALSE; }
 	g_slist_foreach(qecd.param_list, qof_entity_foreach_copy, &qecd);
 	if(qsf_check_error(&qecd)) { return FALSE; }
 	g_slist_free(qecd.param_list);
@@ -687,6 +688,140 @@
 	return TRUE;
 }
 
+struct recurse_s
+{
+	QofSession *session;
+	gboolean   success;
+	GList      *ref_list;
+	GList      *ent_list;
+};
+
+static void
+recurse_collection_cb (QofEntity *ent, gpointer user_data)
+{
+	struct recurse_s *store;
+
+	if(user_data == NULL) { return; }
+	store = (struct recurse_s*)user_data;
+	if(!ent || !store) { return; }
+	store->ent_list = g_list_append(store->ent_list, ent);
+}
+
+static void
+recurse_ent_cb(QofEntity *ent, gpointer user_data)
+{
+	GList      *ref_list, *i, *j, *ent_list, *child_list;
+	QofParam   *ref_param;
+	QofEntity  *ref_ent, *child_ent;
+	QofSession *session;
+	struct recurse_s *store;
+	gboolean   success;
+
+	if(user_data == NULL) { return; }
+	store = (struct recurse_s*)user_data;
+	session = store->session;
+	success = store->success;
+	ref_list = NULL;
+	child_ent = NULL;
+	ref_list = g_list_copy(store->ref_list);
+	if((!session)||(!ent)) { return; }
+	ent_list = NULL;
+	child_list = NULL;
+	i = NULL;
+	j = NULL;
+	for(i = ref_list; i != NULL; i=i->next)
+	{
+		if(i->data == NULL) { continue; }
+		ref_param = (QofParam*)i->data;
+		if(ref_param->param_name == NULL) { continue; }
+		if(ref_param->param_type == QOF_TYPE_COLLECT) {
+			QofCollection *col;
+			col = ref_param->param_getfcn(ent, ref_param);
+			qof_collection_foreach(col, recurse_collection_cb, store);
+			continue;
+		}
+		ref_ent = (QofEntity*)ref_param->param_getfcn(ent, ref_param);
+		if((ref_ent)&&(ref_ent->e_type))
+		{
+			store->success = qof_entity_copy_to_session(session, ref_ent);
+			if(store->success) { ent_list = g_list_append(ent_list, ref_ent); }
+		}
+	}
+	for(i = ent_list; i != NULL; i = i->next)
+	{
+		if(i->data == NULL) { continue; }
+		child_ent = (QofEntity*)i->data;
+		if(child_ent == NULL) { continue; }
+		ref_list = qof_class_get_referenceList(child_ent->e_type);
+		for(j = ref_list; j != NULL; j = j->next)
+		{
+			if(j->data == NULL) { continue; }
+			ref_param = (QofParam*)j->data;
+			ref_ent = ref_param->param_getfcn(child_ent, ref_param);
+			if(ref_ent != NULL)
+			{
+				success = qof_entity_copy_to_session(session, ref_ent);
+				if(success) { child_list = g_list_append(child_list, ref_ent); }
+			}
+		}
+	}
+	for(i = child_list; i != NULL; i = i->next)
+	{
+		if(i->data == NULL) { continue; }
+		ref_ent = (QofEntity*)i->data;
+		if(ref_ent == NULL) { continue; }
+		ref_list = qof_class_get_referenceList(ref_ent->e_type);
+		for(j = ref_list; j != NULL; j = j->next)
+		{
+			if(j->data == NULL) { continue; }
+			ref_param = (QofParam*)j->data;
+			child_ent = ref_param->param_getfcn(ref_ent, ref_param);
+			if(child_ent != NULL)
+			{
+				qof_entity_copy_to_session(session, child_ent);
+			}
+		}
+	}
+}
+
+gboolean
+qof_entity_copy_coll_r(QofSession *new_session, QofCollection *coll)
+{
+	struct recurse_s store;
+	gboolean success;
+
+	if((!new_session)||(!coll)) { return FALSE; }
+	store.session = new_session;
+	success = TRUE;
+	store.success = success;
+	store.ent_list = NULL;
+	store.ref_list = qof_class_get_referenceList(qof_collection_get_type(coll));
+	success = qof_entity_copy_coll(new_session, coll);
+	if(success){ qof_collection_foreach(coll, recurse_ent_cb, &store); }
+	return success;
+}
+
+gboolean qof_entity_copy_one_r(QofSession *new_session, QofEntity *ent)
+{
+	struct recurse_s store;
+	QofCollection *coll;
+	gboolean success;
+
+	if((!new_session)||(!ent)) { return FALSE; }
+	store.session = new_session;
+	success = TRUE;
+	store.success = success;
+	store.ref_list = qof_class_get_referenceList(ent->e_type);
+	success = qof_entity_copy_to_session(new_session, ent);
+	if(success == TRUE) {
+		coll = qof_book_get_collection(qof_session_get_book(new_session), ent->e_type);
+		qof_collection_foreach(coll, recurse_ent_cb, &store);
+	}
+	return success;
+}
+
+
+
 /* ====================================================================== */
 
 /* Specify a library, and a function name. Load the library, 
Index: qofclass.h
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/qofclass.h,v
retrieving revision 1.1.6.5
retrieving revision 1.1.6.6
diff -Lsrc/engine/qofclass.h -Lsrc/engine/qofclass.h -u -r1.1.6.5 -r1.1.6.6
--- src/engine/qofclass.h
+++ src/engine/qofclass.h
@@ -58,6 +58,7 @@
     @brief API for registering paramters on objects 
     @author Copyright (C) 2002 Derek Atkins <warlord at MIT.EDU>
     @author Copyright (C) 2003 Linas Vepstas <linas at linas.org>
+    @author Copyright (c) 2005 Neil Williams <linux at codehelp.co.uk>
 */
 
 #ifndef QOF_CLASS_H
@@ -65,8 +66,14 @@
 
 #include "qofid.h"
 
-/** Core types of objects that can be used in parameters.
- *  Note that QofIdTypes may also be used.  */
+/** \name Core types
+
+Core data types for objects that can be used in parameters.
+Note that QofIdTypes may also be used and will create a 
+single reference between two known objects.
+
+ @{
+ */
 
 #define QOF_TYPE_STRING    "string"
 #define QOF_TYPE_DATE      "date"
@@ -79,10 +86,38 @@
 #define QOF_TYPE_BOOLEAN   "boolean"
 #define QOF_TYPE_KVP       "kvp"
 #define QOF_TYPE_CHAR      "character"
+#define QOF_TYPE_COLLECT   "collection" /**< secondary collections
+are used for one-to-many references between entities and are
+implemented using ::QofCollection.
+These are \b NOT the same as the main collections in the QofBook.
+
+-# Each ::QofCollection contains one or many entities - *all* of a single type.
+-# The entity type within the collection can be determined at run time.
+-# Easy conversions to GList or whatever in the param_setfcn handler.
+-# Each parameter can have it's own collection.
+-# Each entity can have a different *type* of collection to it's siblings, 
+provided that it is acceptable to the set function.
+-# Each object decides which types are acceptable for which parameter in the 
+set functions. This is then part of the API for that object.
+
+QOF_TYPE_COLLECT has two functions, both related to one-to-many 
+links:
+- Represent a reference between 2 entities with a list of acceptable types.
+        (one object linked to many types of single entities)
+- Represent a reference between one entity and many entities of another type.
+        (one object linked to many entities of a single type.)
+
+If the set function can handle it, it could also be used for true one-to-many 
+links: one object linked to many entities of many types.
 
+n.b. Always subject to each collection holding only one type at runtime.
+(otherwise use books).
+
+*/
+/** @} */
 /** Type of Paramters (String, Date, Numeric, GUID, etc.) */
 typedef const char * QofType;
-/** Type of QofParam */
+
 typedef struct _QofParam QofParam;
 
 /** The QofAccessFunc defines an arbitrary function pointer
@@ -220,6 +255,7 @@
 */
 GList* qof_class_get_referenceList(QofIdTypeConst type);
 
+
 #endif /* QOF_CLASS_H */
-/* @} */
-/* @} */
+/** @} */
+/** @} */
Index: kvp_frame.h
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/kvp_frame.h,v
retrieving revision 1.22.4.10
retrieving revision 1.22.4.11
diff -Lsrc/engine/kvp_frame.h -Lsrc/engine/kvp_frame.h -u -r1.22.4.10 -r1.22.4.11
--- src/engine/kvp_frame.h
+++ src/engine/kvp_frame.h
@@ -50,7 +50,8 @@
  * kvp_frame_set_xxx() routines.  Most of the other routines provide
  * only low-level access that you probably shouldn't use.
  
-@{ */
+@{
+*/
 /** @file kvp_frame.h
     @brief A key-value frame system
     @author Copyright (C) 2000 Bill Gribble
@@ -73,30 +74,29 @@
  * KvpValueType enum. */
 typedef struct _KvpValue KvpValue;
 
-/** Enum to enumerate possible types in the union KvpValue 
- *  XXX FIXME TODO: People have asked for boolean values, 
+/** \brief possible types in the union KvpValue 
+ * \todo : People have asked for boolean values, 
  *  e.g. in xaccAccountSetAutoInterestXfer
  *
- * XXX In the long run, this should be synchronized with the 
+ * \todo In the long run, this should be synchronized with the 
  * core QOF types, which in turn should be synced to the g_types
- * in GLib.  Unfortuantely, this requies writing a pile of code
+ * in GLib.  Unfortunately, this requires writing a pile of code
  * to handle all of the different cases.
  * An alternative might be to make kvp values inherit from the 
  * core g_types (i.e. add new core g_types) ??
  */
 typedef enum {
-  KVP_TYPE_GINT64=1,
-  KVP_TYPE_DOUBLE,
-  KVP_TYPE_NUMERIC,
-  KVP_TYPE_STRING,
-  KVP_TYPE_GUID,
-  KVP_TYPE_TIMESPEC,
-  KVP_TYPE_BINARY, 
-  KVP_TYPE_GLIST,
-  KVP_TYPE_FRAME
+  KVP_TYPE_GINT64=1,   /**< QOF_TYPE_INT64  gint64 */
+  KVP_TYPE_DOUBLE,     /**< QOF_TYPE_DOUBLE  gdouble */
+  KVP_TYPE_NUMERIC,    /**< QOF_TYPE_NUMERIC */
+  KVP_TYPE_STRING,     /**< QOF_TYPE_STRING gchar* */
+  KVP_TYPE_GUID,       /**< QOF_TYPE_GUID */
+  KVP_TYPE_TIMESPEC,   /**< QOF_TYPE_DATE */
+  KVP_TYPE_BINARY,     /**< no QOF equivalent. */
+  KVP_TYPE_GLIST,      /**< no QOF equivalent. */
+  KVP_TYPE_FRAME       /**< no QOF equivalent. */
 } KvpValueType;
 
-
 /** \deprecated Deprecated backwards compat tokens
 
 do \b not use these in new code.
@@ -107,14 +107,15 @@
   
 
 /* -------------------------------------------------------- */
-/** @name KvpFrame Constructors */
-/** @{ */
+/** @name KvpFrame Constructors
+ @{
+*/
 
 /** Return a new empty instance of KvpFrame */
 KvpFrame   * kvp_frame_new(void);
 
-/** Perform a deep (recursive) delete of the frame and any subframes. */
-/*
+/** Perform a deep (recursive) delete of the frame and any subframes. 
+
  * kvp_frame_delete and kvp_value_delete are deep (recursive) deletes.
  * kvp_frame_copy and kvp_value_copy are deep value copies. 
  */
@@ -130,18 +131,21 @@
 /** @} */
 
 /* -------------------------------------------------------- */
-/** @name KvpFrame Basic Value Storing */
-/** @{ */
+/** @name KvpFrame Basic Value Storing
+@{
+*/
 
-/** The kvp_frame_set_gint64() routine will store the value of the 
+/**    store the value of the 
  *     gint64 at the indicated path. If not all frame components of 
  *     the path exist, they are created.
  *
- *     Similarly, the set_double, set_numeric, and set_timespec 
- *     routines perform the same function, for each of the respective 
- *     types.
  */
 void kvp_frame_set_gint64(KvpFrame * frame, const char * path, gint64 ival);
+/**
+ *     store the value of the 
+ *     double at the indicated path. If not all frame components of 
+ *     the path exist, they are created.
+*/
 void kvp_frame_set_double(KvpFrame * frame, const char * path, double dval);
 
 /** \deprecated
@@ -149,7 +153,17 @@
 Use kvp_frame_set_numeric instead of kvp_frame_set_gnc_numeric
 */
 #define kvp_frame_set_gnc_numeric kvp_frame_set_numeric
+/**    store the value of the 
+ *     gnc_numeric at the indicated path.
+ *     If not all frame components of 
+ *     the path exist, they are created.
+ */
 void kvp_frame_set_numeric(KvpFrame * frame, const char * path, gnc_numeric nval);
+/**    store the value of the 
+ *     Timespec at the indicated path.
+ *     If not all frame components of 
+ *     the path exist, they are created.
+ */
 void kvp_frame_set_timespec(KvpFrame * frame, const char * path, Timespec ts);
 
 /** \deprecated
@@ -214,12 +228,10 @@
  */
 KvpValue * kvp_frame_replace_value_nc (KvpFrame * frame, const char * slot,
                                        KvpValue * new_value);
-
-
 /** @} */
-
-/** @name KvpFrame URL handling */
-/** @{ */
+/** @name KvpFrame URL handling
+ @{
+*/
 /** The kvp_frame_add_url_encoding() routine will parse the
  *  value string, assuming it to be URL-encoded in the standard way,
  *  turning it into a set of key-value pairs, and adding those to the
@@ -235,8 +247,9 @@
 void     kvp_frame_add_url_encoding (KvpFrame *frame, const char *enc);
 /** @} */
 
-/** @name KvpFrame Glist Bag Storing */
-/** @{ */
+/** @name KvpFrame Glist Bag Storing 
+ @{
+*/
 
 /** The kvp_frame_add_gint64() routine will add the value of the 
  *     gint64 to the glist bag of values at the indicated path. 
@@ -305,10 +318,9 @@
 /** @} */
 
 /* -------------------------------------------------------- */
-/** @name KvpFrame Value Fetching */
-/** @{ */
+/** @name KvpFrame Value Fetching
 
-/** Value accessors.  These all take a unix-style slash-separated 
+  Value accessors.  These all take a unix-style slash-separated 
   path as an argument, and return the value stored at that location.
   If the object at the end of that path is not of the type that was
   asked for, then a NULL or a zero is returned.  So, for example,
@@ -332,6 +344,7 @@
   and then store something else at that path, the string that you've
   gotten will be freed during the store (internally, by the set_*()
   routines), and you will be left hanging onto an invalid pointer.
+@{
 */
 
 gint64      kvp_frame_get_gint64(const KvpFrame *frame, const char *path);
@@ -343,6 +356,7 @@
                                    guint64 * size_return); 
 Timespec    kvp_frame_get_timespec(const KvpFrame *frame, const char *path);
 KvpValue  * kvp_frame_get_value(const KvpFrame *frame, const char *path);
+
 /** Value accessor.  Takes a unix-style slash-separated path as an
  *  argument, and return the KvpFrame stored at that location.  If the
  *  KvpFrame does not exist, then a NULL is returned.
@@ -401,19 +415,19 @@
 
 /** @} */
 /* -------------------------------------------------------- */
-/** @name KvpFrame KvpValue low-level storing routines. */
-/** @{ */
+/** @name KvpFrame KvpValue low-level storing routines.
 
-/** \par You probably shouldn't be using these low-level routines */
+You probably shouldn't be using these low-level routines
 
-/** \par All of the kvp_frame_set_slot_*() routines set the slot values
- *    "destructively", in that if there was an old value there, that
- *    old value is destroyed (and the memory freed).  Thus, one 
- *    should not hang on to value pointers, as these will get 
- *    trashed if set_slot is called on the corresponding key.
- *
- *    If you want the old value, use kvp_frame_replace_slot().
- */
+ All of the kvp_frame_set_slot_*() routines set the slot values
+    "destructively", in that if there was an old value there, that
+    old value is destroyed (and the memory freed).  Thus, one 
+    should not hang on to value pointers, as these will get 
+    trashed if set_slot is called on the corresponding key.
+
+    If you want the old value, use kvp_frame_replace_slot().
+ @{
+*/
 
 /** The kvp_frame_replace_slot_nc() routine places the new value into
  *    the indicated frame, for the given key.  It returns the old
@@ -466,19 +480,20 @@
 /** @} */
 
 
-/** @name KvpFrame KvpValue Low-Level Retrieval Routines */
-/** @{ */
-/** \par You probably shouldn't be using these low-level routines */
-
-/** Returns the KvpValue in the given KvpFrame 'frame' that is 
- *  associated with 'key'.  If there is no key in the frame, NULL
- *  is returned.  If the value associated with the key is NULL, 
- *  NULL is returned.
- *
- *  Pointers passed as arguments into get_slot are the responsibility
- *  of the caller.  Pointers returned by get_slot are owned by the
- *  kvp_frame.  Make copies as needed.
- */
+/** @name KvpFrame KvpValue Low-Level Retrieval Routines
+
+  You probably shouldn't be using these low-level routines
+
+  Returns the KvpValue in the given KvpFrame 'frame' that is 
+  associated with 'key'.  If there is no key in the frame, NULL
+  is returned.  If the value associated with the key is NULL, 
+  NULL is returned.
+
+  Pointers passed as arguments into get_slot are the responsibility
+  of the caller.  Pointers returned by get_slot are owned by the
+  kvp_frame.  Make copies as needed.
+ @{
+*/
 KvpValue   * kvp_frame_get_slot(const KvpFrame * frame, const char * key);
 
 /** This routine return the value at the end of the
@@ -502,14 +517,15 @@
 
 gint          double_compare(double v1, double v2);
 
-/** @name KvpValue List Convenience Functions */
-/** @{ */
-/** \par You probably shouldn't be using these low-level routines */
-
-/** kvp_glist_compare() compares <b>GLists of kvp_values</b> (not to
- *     be confused with GLists of something else):  it iterates over
- *     the list elements, performing a kvp_value_compare on each.
- */
+/** @name KvpValue List Convenience Functions
+
+  You probably shouldn't be using these low-level routines 
+
+ kvp_glist_compare() compares <b>GLists of kvp_values</b> (not to
+ be confused with GLists of something else):  it iterates over
+ the list elements, performing a kvp_value_compare on each.
+ @{
+*/
 gint        kvp_glist_compare(const GList * list1, const GList * list2);
 
 /** kvp_glist_copy() performs a deep copy of a <b>GList of 
@@ -528,14 +544,15 @@
 /** @} */
 
 
-/** @name KvpValue Constructors */
-/** @{ */
-/** \par You probably shouldn't be using these low-level routines */
-
-/** The following routines are constructors for kvp_value.
- *    Those with pointer arguments copy in the value.
- *    The *_nc() versions do *not* copy in thier values, 
- *    but use them directly.
+/** @name KvpValue Constructors
+
+ You probably shouldn't be using these low-level routines
+
+ The following routines are constructors for kvp_value.
+ Those with pointer arguments copy in the value.
+ The *_nc() versions do *not* copy in thier values, 
+ but use them directly.
+ @{
  */
 KvpValue   * kvp_value_new_gint64(gint64 value);
 KvpValue   * kvp_value_new_double(double value);
@@ -587,9 +604,11 @@
 /** @} */
 
 
-/** @name KvpValue Value access */
-/** @{ */
-/** \par You probably shouldn't be using these low-level routines */
+/** @name KvpValue Value access
+
+ You probably shouldn't be using these low-level routines 
+ @{
+*/
 
 KvpValueType kvp_value_get_type(const KvpValue * value);
 
@@ -601,7 +620,7 @@
  *   that this value came from, then this value will be 
  *   uncermoniously deleted, and you will be left pointing to 
  *   garbage.  So don't store values at the same time you are
- *   examining thier contents.
+ *   examining their contents.
  */
 
 gint64      kvp_value_get_gint64(const KvpValue * value);
@@ -656,8 +675,9 @@
  * copying - but more efficient than creating a new KvpValue manually. */
 gboolean kvp_value_binary_append(KvpValue *v, void *data, guint64 size);
 
-/** @name  Iterators */
-/** @{ */
+/** @name  Iterators
+@{
+*/
 /** Traverse all of the slots in the given kvp_frame.  This function
    does not descend recursively to traverse any kvp_frames stored as
    slot values.  You must handle that in proc, with a suitable
Index: qofsession.h
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/qofsession.h,v
retrieving revision 1.2.4.6
retrieving revision 1.2.4.7
diff -Lsrc/engine/qofsession.h -Lsrc/engine/qofsession.h -u -r1.2.4.6 -r1.2.4.7
--- src/engine/qofsession.h
+++ src/engine/qofsession.h
@@ -248,8 +248,8 @@
 
 The recommended backend for the new session is QSF or a future
 SQL backend. Using any of these entity copy functions sets a 
-flag in the backend that this is now a partial QofBook - see 
-below. When you save a session containing a partial QofBook,
+flag in the backend that this is now a partial QofBook.
+When you save a session containing a partial QofBook,
 the session will check that the backend is able to handle the
 partial book. If not, the backend will be replaced by one that
 can handle partial books, preferably one using the same
@@ -323,6 +323,61 @@
 
 gboolean qof_entity_copy_coll(QofSession *new_session, QofCollection *entity_coll);
 
+/** \brief Recursively copy a collection of entities to a session.
+
+\note This function creates a <b>partial QofBook</b>. See 
+::qof_entity_copy_to_session for more information.
+
+The QofBook in the new_session must \b not contain any entities
+with the same GUID as any entities to be copied - there is
+no support for handling collisions - instead, use \ref BookMerge
+
+Objects can be defined solely in terms of QOF data types or
+as a mix of data types and other objects, which may in turn
+include other objects. These references can be copied recursively
+down to the third level. e.g. ::GncInvoice refers to ::GncOwner which
+refers to ::GncCustomer which refers to ::GncAddress. See
+::QofEntityReference.
+
+\note This is a deep recursive copy - every referenced entity is copied
+to the new session, including all parameters. The starting point is all
+entities in the top level collection. It can take some time.
+
+ at param coll A QofCollection of entities that may or may not have 
+references.
+
+ at param session The QofSession to receive the copied entities.
+
+ at return TRUE on success; if any individual copy fails, returns FALSE.
+<b>Note</b> : Some entities may have been copied successfully even if
+one of the references fails to copy.
+
+*/
+gboolean
+qof_entity_copy_coll_r(QofSession *new_session, QofCollection *coll);
+
+/** \brief Recursively copy a single entity to a new session.
+
+Copy the single entity and all referenced entities to the second level.
+
+Only entities that are directly referenced by the top level entity are
+copied.
+
+This is a deep copy - all parameters of all referenced entities are copied. If 
+the top level entity has no references, this is identical to 
+::qof_entity_copy_to_session.
+
+ at param ent A single entity that may or may not have references.
+
+ at param session The QofSession to receive the copied entities.
+
+ at return TRUE on success; if any individual copy fails, returns FALSE.
+<b>Note</b> : Some entities may have been copied successfully even if
+one of the references fails to copy.
+*/
+gboolean
+qof_entity_copy_one_r(QofSession *new_session, QofEntity *ent);
+
 /** @} 
 */
 
@@ -330,16 +385,17 @@
 
 Part of the handling for partial books requires a storage mechanism for
 references to entities that are not within reach of the partial book.
-This requires a hash table in the book data to contain the reference 
+This requires a GList in the book data to contain the reference 
 QofIdType and GUID so that when the book is written out, the
 reference can be included. See ::qof_book_get_data. 
-When the file is imported back in, the hash table needs to be rebuilt.
+
+When the file is imported back in, the list needs to be rebuilt.
 The QSF backend rebuilds the references by linking to real entities. Other
 backends can process the hash table in similar ways.
 
-The hashtable key is the GUID of the known entity and the value is a 
-QofEntityReference to the referenced entity - a struct that contains the
-GUID and the QofIdType of the referenced entity.
+The list stores the QofEntityReference to the referenced entity -
+a struct that contains the GUID and the QofIdType of the referenced entity 
+as well as the parameter used to obtain the reference.
 
 Partial books need to be differentiated in the backend, the 
 flag in the book data is used by qof_session_save to prevent a partial


More information about the gnucash-changes mailing list