6.2. Draft of a rule set framework.

A rough draft of a rule set

  1. Validate the import book.

    To generate the ruleset, qof_book_merge will parse all objects and all parameters in the import book. If any parameters contain unexpected data, illegal values, non-existent parameter types etc., qof_book_merge will use the reserved value of INVALID and will immediately abort, freeing the import book in memory. The target book will be untouched and an error code will be returned. Procedures that call qof_book_merge code must check the error value set by qof_book_mergeInit. Any parameters or objects that are not correctly described for qofclass at compile time will be ignored.

  2. Identify the GUID.

    If a GUID match exists, this is a semantic match - the two objects are the same, even if the data content has changed. The object will be tagged as an ABSOLUTE match if there are no changes in any relevant object parameters. See Preparation for the discussion of what parameters are relevant and why. If any relevant parameters differ, the object is tagged as UPDATE. If the import contains an account with the same GUID, for example, a change in the account description would be inserted into the target book (overwriting the previous value). Semantic matches are not reported to the user. ABSOLUTE matches will be ignored - leaving the target book untouched.

    If the import object GUID does not match an existing object, the parameter values of the object are compared to other objects of the same type in the target book. If the same data exists in the target book with a different GUID, the object is tagged as DUPLICATE. All objects tagged as DUPLICATE are ignored in the final merge. If the data has changed, the object is tagged as REPORT.

  3. Identifying fields that may result in a collision.

    To determine whether an object should be tagged as ABSOLUTE or UPDATE, DUPLICATE or REPORT, the parameter data within each object needs to be compared with the target book to check for a collision. qof_book_merge queries the target book for all objects of the same object type and compares the parameter values.

    The object type can be easily determined as a result of the QOF framework; objects are passed to the qof_book_merge comparison routine in type order. Therefore, a single check on all existing objects of the same type in the target book is required to see if an object exists that would cause a collision.

  4. Tag remaining objects

    Any objects that cannot be tagged as ABSOLUTE, UPDATE, DUPLICATE or REPORT are tagged as NEW. These contain GUID's that do not exist in the target book and contain no data that conflicts with existing objects of the same type. Unlike semantic matches, a NEW object does not overwrite existing data in the target book. An Account object, for example, tagged as NEW, will be inserted into the target book as a new account. Transactions tagged as NEW will be appended to the appropriate account. Objects tagged as NEW are not reported to the user.

  5. User Intervention.

    All REPORT items are made available to the user intervention dialog which then controls how each conflict is resolved. All REPORT items need to be re-assigned to UPDATE, DUPLICATE or NEW. In order to maintain the integrity of the amended target book, objects in the import book cannot be ignored. Note that because import data must be passed to qof_book_merge as a GNCBook*, certain default data will be created. Allowing certain objects to be ignored could lead to unexpected results.

    If the user aborts the dialog, the dialog control procedure should set the last/current object to INVALID - this will cause qof_book_merge to abort without changing the target book. Until all REPORT items have been re-assigned, the target book remains unchanged.

  6. Altering the target book

    All objects tagged as ABSOLUTE or DUPLICATE are ignored. Before processing the objects tagged as UPDATE, all NEW objects will be added to the target book.

The rule set itself is built from the QOF registration definitions of each component of the book. GnuCash uses QOF to provide all internal query functions. QoF in turn uses definitions within the code of each object to determine the kind of data contained in the object and how that data can be addressed and modified.

static QofObject commodity_table_object_def =
{
  interface_version: QOF_OBJECT_VERSION,
  e_type:            GNC_ID_COMMODITY_TABLE,
  type_label:        "CommodityTable",
  book_begin:        commodity_table_book_begin,
  book_end:          commodity_table_book_end,
  is_dirty:          NULL,
  mark_clean:        NULL,
  foreach:           NULL,
  printable:         NULL,
};

gboolean
gnc_commodity_table_register (void)
{
  gnc_quote_source_init_tables();
  return qof_object_register (&commodity_table_object_def);
}
	

A more complex definition will help to show how the rule sets from the first draft will be applied into the existing QoF structures:

static QofObject gncInvoiceDesc =
{
  interface_version:  QOF_OBJECT_VERSION,
  e_type:             _GNC_MOD_NAME,
  type_label:         "Invoice",
  book_begin:         NULL,
  book_end:           NULL,
  is_dirty:           qof_collection_is_dirty,
  mark_clean:         qof_collection_mark_clean,
  foreach:            qof_collection_foreach,
  printable:          _gncInvoicePrintable,
};
gboolean gncInvoiceRegister (void)
{
  static QofParam params[] = {
    { INVOICE_ID, QOF_TYPE_STRING, (QofAccessFunc)gncInvoiceGetID, NULL },
    { INVOICE_OWNER, GNC_ID_OWNER, (QofAccessFunc)gncInvoiceGetOwner, NULL },
    { INVOICE_OPENED, QOF_TYPE_DATE, (QofAccessFunc)gncInvoiceGetDateOpened, NULL },
    { INVOICE_DUE, QOF_TYPE_DATE, (QofAccessFunc)gncInvoiceGetDateDue, NULL },
    { INVOICE_POSTED, QOF_TYPE_DATE, (QofAccessFunc)gncInvoiceGetDatePosted, NULL },
    { INVOICE_IS_POSTED, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncInvoiceIsPosted, NULL },
    { INVOICE_IS_PAID, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncInvoiceIsPaid, NULL},
    { INVOICE_BILLINGID, QOF_TYPE_STRING, (QofAccessFunc)gncInvoiceGetBillingID, NULL },
    { INVOICE_NOTES, QOF_TYPE_STRING, (QofAccessFunc)gncInvoiceGetNotes, NULL },
    { 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_TYPE, QOF_TYPE_STRING, (QofAccessFunc)gncInvoiceGetType, NULL },
    { INVOICE_TERMS, GNC_ID_BILLTERM, (QofAccessFunc)gncInvoiceGetTerms, NULL },
    { INVOICE_BILLTO, GNC_ID_OWNER, (QofAccessFunc)gncInvoiceGetBillTo, NULL },
    { QOF_QUERY_PARAM_ACTIVE, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncInvoiceGetActive, NULL },
    { QOF_QUERY_PARAM_BOOK, QOF_ID_BOOK, (QofAccessFunc)qof_instance_get_book, NULL },
    { QOF_QUERY_PARAM_GUID, QOF_TYPE_GUID, (QofAccessFunc)qof_instance_get_guid, NULL },
    { NULL },
  };

  qof_class_register (_GNC_MOD_NAME, (QofSortFunc)gncInvoiceCompare, params);
  reg_lot ();
  reg_txn ();

  return qof_object_register (&gncInvoiceDesc);
}
	

Note that the initial config of Account had a NULL where the QofSetterFunc could have been declared.

A problem arises in Transaction where a transaction is voided. There are three QofAccessFunc routines for each part of the void but only one function that could be used as a QofSetterFunc which sets all three values, as well as a second set function that undoes the void operation.

Invoice: unable to set the due date - no set_fcn?

Lots are also potential problems - this will need to be monitored.

In Jobs, gncJobGetActive is defined in two separate parameters?

The files in use with QofSetterFunc are: