5.2. Mapping QSF objects between QOF applications

5.2.1. The QSF Map file

qsf-map is an XML format used to map a series of QOF objects from one application to a series of different QOF objects in another application. The map works by defining the steps of the calculations involved, including conditionals, pattern matching, defaults, simple assignments and arithmetic operators. The example snippet below does not include all necessary data for a working map for reasons of brevity but does try to include at least one example of each QSF map tag and construct.

1:  <definition qof_version="3">
2:    <define e_type="qof-expenses">Pilot-link QOF expenses</define>
3:    <define e_type="qof_datebook">Pilot-link QOF datebook</define>
4:    <define e_type="gncInvoice">Invoice</define>
5:    <define e_type="Trans">Transaction</define>
6:    <define e_type="gncEntry">Order/Invoice/Bill Entry</define>
7:    <default name="mileage_rate" type="numeric" value="28/100"/>
8:  </definition>
9:  <object type="gncEntry">
10:   <calculate type="string" value="action">
11:   <if type="qof-expenses">
12:     <set>Material</set>
13:   </if>
14:   <else type="qof-datebook">
15:     <set>Hours</set>
16:   </else>
17:   </calculate>
18:   <calculate type="numeric" value="iprice">
19:     <if type="string" value="expense_type">
20:       <equals type="string" value="etMileage">
21:         <set>mileage_rate</set>
22:       </equals>
23:     </if>
24:   </calculate>
25:   <calculate type="string" value="desc">
26:     <if boolean="use_weekday_descriptor">
27:       <set format="%A">qsf_time_string</set>
28:    </if>
29:    <else>
30:      <set object="qof-expenses">expense_vendor</set>
31:    </else>
32:   </calculate>
33: </object>

Preset values. Some default values are always defined:

Line 1: Interface Version. All QOF objects used in the map must use the same QOF interface version. Back

Line 2 to 6: Definitions. The definition tag defines the objects from both applications that will be used in the map. The e_type and descriptive contents of each description must appear exactly as defined in the QofObject definitions within the relevant application. Defined objects must also be unique: the e_type values and descriptive contents from different applications must not overlap within the map. If there is a name collision between two applications, a request should be made to the application developers to modify the settings of one of the two applications. If subsequent lines define suitable calculations, the map can be used in either direction. Back

Line 7: Defaults. Defaults are independent of object definitions. The calculations can assign the default to any parameter that matches the required QOF type. Back

Line 9: Map Objects. Each defined object must have one calculation defined in the map for each QofClass parameter that has both a get() and a set() routine defined as not NULL (a principle established with qof_book_merge). Each calculation must end with a set tag. Empty calculations for required parameters will cause the map to fail. Back

Line 10: Calculations. Each calculation is defined by specifying the type and name of the parameter in the current output object to be SET. The map is processed by the receiving application which understands the output object. Back

Line 11: Conditionals. Conditionals can reference objects as if within the original application. In operation, the map is overlaid across both sets of defined objects, an import object in the source application and an output object for the destination object. The current import and output QSF objects are therefore always available to the map. This conditional specifies that if the type of the current import QSF object is qof-expenses, the current output gncEntry object should use the action parameter defined within the conditional. Conditionals can reference parameter as well as object values. Back

Line 12: Assignments. Map assignments can use the native values within the output object. The output object must support setting the relevant parameter using the value exactly as given in the map because the relevant set() function will be called using this value. This may reduce the readability of the map but the relevant application could also be modified to support a more readable set function. Back

Line 14: Nesting. if(){} else{} is also supported. Nesting of conditionals causes problems for validating the final map against any sensible XML Schema and a map that doesn't validate will be rejected. When editing conditionals in a QSF map, ALWAYS validate the map using xmllint. If necessary, define a variable at the foot of the definitions block, using a similar syntax to a default, then use that variable in another conditional. Back

<variable name="my_rate" type="numeric" value="0/1"/>

Line 21: Conversions. A value of one type can be used to set a suitable value of a different parameter with a different type but converting between types is NOT supported. You must define a function within one of the applications that can handle the conversion from the relevant import type. Any request to set a parameter with a value that does not match the required type will be ignored. The only exceptions to this are for GUID's and enumerator values. Back

Both GUID's and enumerator values use the option attribute of the set tag. This allows QSF to do these strictly limited type conversions before calling the set function for the parameter.

Line 26: Boolean preferences. Use a default boolean value to adjust strings like descriptions and notes according to user preference. Back

Line 30: QSF objects. When the map needs to draw in a real value from the QSF objects, use the type attribute to specify the QSF object to use. Back

An example QSF map file, pilot-qsf-GnuCashInvoice.xml, and the QSF Map Schema are now available.

5.2.2. Relating the map to the QSF objects

The simple QSF parser shows how the QSF map can be built into a xmlHashTable (the QOF version of this parser will use GHashTable which has certain advantages) that can be used to lookup the calculation needed for each tag in the output QSF object file. The parser reads the input QSF object file into a hash then iterates over the QSF map to build the definitions and default hashes. Each calculation is then parsed to create the output tags in a new QSF XML tree. When all calculations are complete, the output tree is written to the output file using xmlDocDump.

The pilot-qsf-GnuCashInvoice QSF map is not yet complete - none of the datebook or address data is currently handled. The QSF map Schema and format may change slightly during this process - development is ongoing.