5.3. Writing new QSF maps

Until tools can be developed to design the map, each QSF map will have to be edited by hand. The process requires detailed knowledge of the QOF objects of both applications as well as a general understanding the source code for each program. This section contains the notes I used to create the pilot-qsf-GnuCashInvoice QSF map.

The expenses QOF object in pilot-link can be described as follows. This text is copied directly from the QofObject and QofClass parameter definitions in the source code.

struct tm date	 		EXP_DATE,	QOF_TYPE_DATE,
enum ExpenseType type		EXP_TYPE,	QOF_TYPE_INT32,
enum ExpensePayment payment	EXP_PAYMENT,	QOF_TYPE_INT32,
int currency			EXP_CURRENCY,	QOF_TYPE_INT32,
char *amount			EXP_AMOUNT,	QOF_TYPE_STRING,
char *vendor			EXP_VENDOR,	QOF_TYPE_STRING,
char *city			EXP_CITY,	QOF_TYPE_STRING,
char *attendees			EXP_ATTENDEES,	QOF_TYPE_STRING,
char *note			EXP_NOTE,	QOF_TYPE_STRING,

enum ExpenseType {
	etAirfare, etBreakfast, etBus, etBusinessMeals,
	etCarRental, etDinner,
	etEntertainment, etFax, etGas, etGifts, etHotel,
	etIncidentals,
	etLaundry,
	etLimo, etLodging, etLunch, etMileage, etOther, etParking,
	etPostage,
	etSnack, etSubway, etSupplies, etTaxi, etTelephone, etTips,
	etTolls,
	etTrain
};

enum ExpensePayment {
	epAmEx, epCash, epCheck, epCreditCard, epMasterCard,
	epPrepaid, epVISA,
	epUnfiled
};


Note that although a struct tm in the code, date is considered as a standard QOF_TYPE_DATE - a Timespec. The use of an integer for currency and a string for the amount could also cause problems in GnuCash.

Datebook is defined as follows (items not used in the map omitted):

int event; /* Is this a timeless event?  */
	DATEBOOK_EVENT, QOF_TYPE_INT32,
struct tm begin; /* When does this appointment start? */
	DATEBOOK_BEGIN, QOF_TYPE_DATE,
struct tm end; /* When does this appointment end? */
	DATEBOOK_END, QOF_TYPE_DATE,
enum repeatTypes repeatType;
/* How should this appointment be repeated, if at all?*/
	DATEBOOK_REPEAT_TYPE, QOF_TYPE_INT32,
int repeatForever;
/* Do the repetitions end at some date or carry on forever? */
	DATEBOOK_REPEAT_FOREVER, QOF_TYPE_INT32,
	(this may be changed to QOF_TYPE_BOOLEAN).
struct tm repeatEnd;	/* What date do they end on? */
	DATEBOOK_REPEAT_END, QOF_TYPE_DATE,
int repeatFrequency;
/* Should I skip an interval for each repetition? */
	DATEBOOK_REPEAT_FREQUENCY, QOF_TYPE_INT32,
enum DayOfMonthType repeatDay;	/* for repeatMonthlyByDay  */
	DATEBOOK_REPEAT_DAY, QOF_TYPE_INT32,
int repeatWeekstart;
/* What day did the user decide starts the week? */
	DATEBOOK_REPEAT_WEEK_START, QOF_TYPE_INT32,
int exceptions;
/* How many repetitions are there to be ignored? */
	DATEBOOK_EXCEPTIONS, QOF_TYPE_INT32,
struct tm *exception;	/* What are they? */
	DATEBOOK_EXCEPTION, QOF_TYPE_DATE,
char *description;
/* What is the description of this appointment? */
	DATEBOOK_DESCRIPTION, QOF_TYPE_STRING,
char *note;	/* Is there a note to go along with it?    */
	DATEBOOK_NOTE, QOF_TYPE_STRING,

Timeless events are anniversaries, birthdays or other marker events that are set in the PDA as 'No time'. If event is set, the datebook object needs to be ignored for the purposes of an invoice.

begin and end will be used to calculate the number of hours to charge on the invoice. end - begin maps to gncEntry::ENTRY_QTY.

Repeating events may need care to identify the actual date of the event. Check the repeatType: repeatNone, repeatDaily, repeatWeekly, repeatMonthlyByDay, repeatMonthlyByDate or repeatYearly. Check the repeatForever value and the repeatEnd Timespec. Check the repeatFrequency, repeatDay and repeatWeekStart only if appropriate. If still within the repeatEnd, check if any dates are to be ignored and find out which ones. Finally, if the event survives all checks, map to gncInvoice::INVOICE_OPENED and gncEntry::ENTRY_DATE, gncEntry::ENTRY_DATE_ENTERED. (Other maps are free to map to just one or two of those.)

Description should match with the gncJob but this is a simple check.

Some items are not read by pilot-link as of 0.11.8. There is a category setting and a Location setting. User intervention will be required to confirm that the correct event has been selected, in the absence of either of these settings, if there is any mis-match in the Description.

Finally for this map, the AddressBook object is defined as a list of all strings:

(concat(ADDR_FIRST_NAME,ADDR_LAST_NAME)	<==> ADDRESS_NAME ||
ADDR_COMPANY,				<==> ADDRESS_NAME)
ADDR_PHONE_ONE,		<==> 	ADDRESS_PHONE	(if regexp matches)
ADDR_PHONE_TWO,		<==>	ADDRESS_FAX	(if regexp matches)
ADDR_PHONE_THREE,	<==>	ADDRESS_EMAIL	(if regexp matches)
ADDR_PHONE_FOUR,
ADDR_PHONE_FIVE,
ADDR_ADDRESS,		<==>	ADDRESS_ONE
ADDR_CITY,		<==>	ADDRESS_TWO
ADDR_STATE,		<==>	ADDRESS_THREE
ADDR_ZIP,		<==>	ADDRESS_FOUR	(if regexp matches)
ADDR_COUNTRY,
ADDR_TITLE,
ADDR_CUSTOM_ONE,
ADDR_CUSTOM_TWO,
ADDR_CUSTOM_THREE,
ADDR_CUSTOM_FOUR,
ADDR_NOTE,
ADDR_CATEGORY,

Note how this map is not fixed. In the last summary, I've tried to summarise the process using familiar symbols. The map will be intelligent and will always check the incoming content against a regular expression of what the mapped field normally contains. In most cases, an address is only created once. More commonly, the details of the address will be used to check against the existing addresses to locate the correct customer. As invoices will be almost always new items, a GUID check is not practical. It will also be possible to map the custom fields on a per-user basis, perhaps to contain the rates to charge etc.