Other FPI Links: FPI Server Guide
Downloads: FPIClientSDK.zip | FPIServerSDK.zip


 

Financial Posting Interface - Client Guide

© 2001 Quik Sense Software

By Scott K. Maxwell

The Financial Posting Interface allows any application to post transactions to any FPI-compliant account manager. This document will tell you how to interact with an FPI-compliant account manager or budget manager.

FPI was created by Quik Sense Software as a generic way to post transactions to account managers and to update those transactions if necessary. I created FPI primarily to interface my Quik Budget product to account managers. Through FPI, users can create and modify transactions in Quik Budget and the same transactions will be created and modified in the user's account manager of choice. This avoids the problem of having to enter the transaction into multiple applications. However, FPI is not for Quik Budget alone.Through FPI, any application can post transactions with no knowledge of the internal workings of the current account manager. Quik Budget is also an FPI server so with one minor change you can post to Quik Budget as well.

This document is aimed at authors or any application that could benefit from the ability to post transactions to the user's default account or budget manager.

Features of FPI

Clients can:

  • Interact with any FPI application with no knowledge of the specific application.
  • Get a list of accounts, transaction types and classes.
  • Query the next check number for any given account.
  • Set the last check number used for any given account.
  • Get a list of categories and sub-categories. FPI supports an unlimited nesting level for sub-categories.
  • Query the base currency. This can be used to verify that both apps are using the same home currency.
  • Post transactions. The FPI application returns a 32-bit UID if it was able to complete the post.
  • Query transactions. This is done via the UID generated by the Post method so the client will only be looking at transactions that it created.
  • Modify transactions. This is also done via the UID generated by Post.

Getting the FPI Client SDK

All of the code necessary for implementing FPI clients is available in the FPI Client SDK at http://quiksense.com/FPI/FPIClientSDK.zip. You probably want to extract the SDK into your main project directory. You will need to add PlugInBase.cpp and FFP.cpp to your project.source code.

The FPI Client SDK is a subset of the FPI Server SDK so if you have already installed the FPI Server SDK, you do not need to download the client code.

Loading the Plug-In

To try to load the plug-in do something like this:

AccountManager* accountManager = AccountManager::Lock('CRID',"My Program",true,0,'FPAM',0,'FPAM'); 

The parameters are:

  1. Creator ID of client.
  2. Database name of client
  3. User interface OK. Pass true if the plug-in is allowed to display UI elements.
  4. Bitmap of transaction types that the client is responsible for. The types are listed in FinancialAppClasses.h. These are specified to allow for chaining of plug-ins. OR together as many transaction types as you handle to indicate to the plug-in that it should not try to handle those types. For instance, Quik Budget passes in BUDGETMANAGER_CLASS so that the plug-in will know not to try to chain to the budget manager.
  5. type specifies the type of database to search for to find the plug-in.
  6. creator specifies the creator of the database to search for. Specify zero to search for any creator.
  7. rscType specifies the resource type to search for within the database.
  8. rscID specifes the resource ID to search for within the database.

In most cases you will want to use the above line as is, replacing only the first two parameters. One exception is if you are trying to connect to the default budget manager (usually Quik Budget) instead of the default account manager. In that case, you would specify 'FPBM' for both the type and rscType.

Some plug-ins are built into their main application database so you may wonder how this call will find them. The answer is in a bit of glue magic. The Load method searches for the 'FPAM' database. When it finds it, first it checks to see if the database is a resource database. If so, it uses it as the plug-in. If not, it reads the first 8 bytes from record zero to discover the type/creator of the actual plug-in database. It is the account manager's responsibility to create a database of type 'FPAM' that contains its type/creator.

Financial Fixed-Point

FPI uses Financial Fixed-Point (FFP) to specify all amounts. Internally, FFP consists of an Int32 and a UInt16. The UInt16 specifies how many decimals of precision are needed. This will almost always be either zero or two. $20.00 is represented as an Int32 of 2000 and a UInt16 of 2. 2000 lira is represented as an Int32 of 2000 and a UInt16 of 0. This allows for a good range of values, very high precision and good performance. You can create FFP objects from either integers or doubles.

To create an FFP object representing $98.47, you would do this:

FFP amount(9847,2);	// Constructor taking an Int32

Or:

FFP amount(98.47,2);	// Constructor taking a double

You can query the value with 4 methods:

    
    	
  • double AsDouble() const; // Returns the value as a double
  • Int32 Value() const; // Returns the value stored in the Int32 portion
  • UInt16 Decimals() const; // Returns the number of decimal places to shift the Int32
  • char *AsString(char *buf, char decimalPoint='.') const; // Prints the value to the buffer

Posting

In many cases, the only thing an application will need to do is post transactions. That is done very simply with the Post method. Here is an example:

if (accountManager) {	// Make sure we have a valid plug-in
FFP amount(-2000,2); // -$20.00 UInt32 uid = accountManager->Post( 0, //Link ID "Checking", //Account name 0, //Transfer account name TimGetSeconds(), //Time of transaction "Kentucky Fried Chicken", //Comment "Large family bucket", //Note "ATM", //Transaction number or type "Dining", //Category "Personal", //Class false, //Cleared false, //Private amount, //Amount 1.0, //Exchange rate "$", //Currency symbol "USD", //Currency code fpiShowUIIfIncomplete); //UI flag }

If the post is successful, it will return a unique ID that can be used to retrieve the transaction again later. If the post fails, it will return 0.

The first parameter is a link ID. This would be a unique ID for the account manager to store if it wants to directly access the matching client record. If the plug-in does not know how to handle records from the client app, this should probably be zero. In the case of Quik Budget, the link ID is always the unique ID of the QB transaction that corresponds to this post. When QB posts transactions to an account manager, you always end up with the transaction existing in both the account manager and in QB.

If you post to an account that does not exist, it is up to the account manager to decide what to do. It could create the account. If UI is allowed, it could ask the user if they want to create the account. Or it could just quit and return a unique ID of zero to indicate failure.

The transferToAccount is specified if the transaction is a transfer.

comment is often used for payee.

note can generally be pretty large and can contain anything.

The transaction number or type is used to indicate a check number, transaction number, or type of transaction such as 'ATM', 'Withdrawal', 'Deposit', etc.

Category capability varies greatly among account managers. Some only have a single level of categories. Others allow for a second level or even unlimited levels of nested categories. In FPI, these are always represented as a single string with the different levels separated by a colon. For instance, "Dining:Lunch" would indicate the Lunch sub-category of Dining. It is entirely up to the account manager how it will handle categories. If the category has more levels than the account manager represents, it could just chop off the extra levels, create a new category containing the whole string or simply fail and return a 0. Ideally the account manager will not fail.

Some account managers support transaction classes as well as categories. This is often used to specify 'Business' and 'Personal' or to specify the name of the person who spent the money.

Nearly all account managers support the concept of cleared transactions.

Only a few account managers support the concept of private transactions.

When specifying the amount of the transaction, be sure to always specify a negative value for money spent, charged or withdrawn and a positive value for money deposited or credited. Expense amounts are always specified in Financial Fixed-Point as detailed above.

The next three parameters are completely optional. They are rate, curSymbol, and curCode. The exchange rate is always a double representing the number to multiply the amount by to get the total in the native currency. If the native currency is US$, the transaction currency is British Pounds and the amount is 10 pounds, an exchange rate of 1.6 would indicate US$16.00. You can specify both the symbol and the code of the currency as different account managers and different users prefer to use one over the other. The symbol is the common symbol used for that currency such as '$'. The code is the 3-letter code used to indicate the currency such as 'USD'.

The final parameter is also optional. It is the UIFlag. Valid values are fpiShowUINever, fpiShowUIAlways, and fpiShowUIIfIncomplete. The default is fpiShowUIIfIncomplete. Some account managers will ignore this parameter and never show UI. No account manager should ever show UI, though, if you specify fpiShowUINever or if you specified false for the UIIsOkay flag in the account manager constructor.

Getting a Handle on a Transaction

After you have posted a transaction, you may want to query or modify it later. To do that you need to create a handle to the transaction. That is done like this:

if (accountManager) {	// Make sure we have a valid plug-in.
AMTransaction amTran(accountManager,uid,true);
if (amTran.Valid()) {
// Do stuff with it }
}

The uid is the one that you received previously from the Post method. The third parameter is true for writable and false for read-only. After you instantiate the AMTransaction object, always check to see if it is still Valid. An invalid transaction could mean anything but it most often indicates that the user deleted or purged the transaction.

Querying a Transaction

Once you have a valid handle, you can use the Get method to retrieve the details. Get allows you to query all of the information that you set in the Post method and has almost exactly the same prototype. The main difference is that everything is a pointer. For instance, instead of:

const char *comment
Get expects:
const char **comment

This is very similar to the way DmRecordInfo and DmDatabaseInfo work. You simply pass in valid pointers for the items you wish to retrieve and NULL for those you don't need. All of the parameters default to NULL so if you only need to retrieve the comment field, you can do this:

if (accountManager) {	// Make sure we have a valid plug-in.
AMTransaction amTran(accountManager,uid,true);
if (amTran.Valid()) { const char *comment; Err err = amTran.Get(NULL,NULL,NULL,&comment); //account,transfer,seconds,comment // Do something with the data.
}
}

The string data is managed by the AMTransaction object so be sure to copy the data to a safe location if you need it after the AMTransaction object's destructor is called.

Modifying a Transaction

The Change method looks almost exactly like the Post method. It works in a very similar way to DmSetRecordInfo and DmSetDatabaseInfo. You simply pass in valid pointers for the items you wish to modify and NULL for those you don't need. All of the parameters default to NULL so if you only need to change the comment field, you can do this:

if (accountManager) {	// Make sure we have a valid plug-in.
AMTransaction amTran(accountManager,uid,true);
if (amTran.Valid()) { const char *comment = "Rubios"; uid = amTran.Change(NULL,NULL,NULL,NULL,comment); //linkID,account,transfer,seconds,comment }
}

Change returns UInt32. The Change method could cause the transaction to be removed or to change unique ID. For instance, in Quik Budget, changing the category to one that does not exist as a QB wallet would cause QB to remove the transaction and return a UID of 0. Also, some account managers have multiple databases depending on the class or account of the transaction so the transaction could end up moving to a completely different database with a different unique ID seed.

While it is possible to change only the amount or only the exchange rate, if you are dealing in foreign currencies, it is usually safest to pass in valid values for amount, rate and at least curSymbol. The reason for this is that some account managers don't bother to store the exchange rate and currency for the transaction so passing only one of these values could cause the account manager to do something unexpected.

Adding a Split

Once you have created a transaction, you may want to add splits to it. This is done with the AddSplit method. Here is an example:

if (accountManager) {	// Make sure we have a valid plug-in.

	FFP amount(-6.92,2);	// -$6.92

	UInt32 uid = accountManager->Post(

								0,							//Link ID

								"Checking",					//Account name

								0,							//Transfer account name

								TimGetSeconds(),			//Time of transaction

								"Safeway",					//Comment

								"Bread, Cheese, Mushrooms",	//Note

								"1520",						//Check number

								"Groceries",				//Category

								"Personal",					//Class

								false,						//Cleared

								false,						//Private

								amount);					//Amount

	if (uid) {

		AMTransaction amTran(accountManager,uid,true);
if (amTran.Valid()) { amount.Set(-3.08,2); uid = amTran.AddSplit( 0, // Link ID "Paper towels", // Split note "Household", // Category "Personal", // Class amount); // Amount of split } }
}

The resulting transaction would be for $10 (6.92+3.08) and contain a split with two parts. Whenever you add a split, the original transaction becomes split 1 and the first AddSplit becomes split two.

Notice that the AddSplit method takes a linkID and returns a unique ID. The reason for this is that each split can be accessed independently. For example, Quik Budget does not have true split support so the QB AddSplit method simply creates another transaction. An account manager with true split support could keep a separate QB link ID for each split. An account manager with true splits could return a different unique ID for each split by ORing the 24-bit transaction uid with an 8-bit split uid.

Account Methods

FPI provides methods for querying various features of the account manager. For accounts, FPI provides:

UInt16 AccountIndexOf(const char *name);

const char* GetAccountName(UInt16 index);

UInt16 AccountCount();
const char** GetAccountList(UInt16 _All_RscID, UInt16 _None_RscID);

Often you will want to get an index of the account in order to use the transaction number methods or simply to set the selection value for a list of accounts. AccountIndexOf will take a name and return a list index. If the account does not exist, it will return EOF (0xffff). GetAccountName converts that index back into a name. The memory is managed by the plug-in.

AccountCount does as you would expect and returns the number of valid accounts.

GetAccountList returns a list of the valid accounts. This list can be passed directly to LstSetChoices.
WARNING: The client needs to do MemPtrFree on the block of data returned by GetAccountList when the data is no longer needed.

Transaction Number/Type Methods

The Post and Change method allow you to specify a single string for number. This string can be either a number representing the transaction or check number, or it may be a string representing an account manager specific transaction type string. To find out what kind of strings the account manager expects, you can use the following methods:

bool	SupportsTransactionNumber() const;

bool	AllowAnythingInTransactionNumber() const;

Int32	GetAccountNextCheckNumber(UInt16 acctIndex);

void	SetAccountLastCheckNumber(UInt16 acctIndex, Int32 last);
UInt16 TransactionNumberStringCount(UInt16 acctIndex);
const char** GetTransactionNumberList(UInt16 acctIndex);

Some account managers do not support transaction numbers/types at all. In that case, you may want to change your user interface to not allow the user to enter data that won't be preserved anyway.

AllowAnythingInTransactionNumber returns true if the account manager is able to store any arbitrary string for the type. False indicates that the account managers will only take expected strings. In Quik Budget, I don't allow the user to enter random text unless the account manager supports it.

GetAccountNextCheckNumber will return the next check number for the specified account. Not all account managers store this. You can also call SetAccountLastCheckNumber to update the value.

TransactionNumberStringCount returns the number of transaction types for the given account.

GetTransactionNumberList returns a list of transaction type strings for the given account. This list can be passed directly to LstSetChoices.
WARNING: The client needs to do MemPtrFree on the block of data returned by GetTransactionNumberList when the data is no longer needed.

Category Methods

FPI supports infinitely nested category structures. To accomodate this, each of the category methods takes a root string. The prototypes look like this:

bool			HasCategories(const char *root=NULL);

UInt16			CategoryCount(const char *root=NULL);

const char**	GetCategoryList(const char *root=NULL);

If you pass NULL to any of these methods, they will return data based on the root categories. If you pass a root string, the return values will refer to sub-categories of that root. For instance, to query the number of sub-categories of "Dining", you would do:

UInt16 count = CategoryCount("Dining"); 

WARNING: As with the Account and Transaction Number methods, the client needs to do MemPtrFree on the block of data returned by GetCategoryList when the data is no longer needed.

Class Methods

Classes are similar to categories but they are not usually nested. Classes seem to be supported by about half of the account managers currently available for the Palm platform. FPI provides the following:

bool			SupportsClasses() const

UInt16			ClassCount();

const char**	GetClassList();

WARNING: As with the Account and Transaction Number methods, the client needs to do MemPtrFree on the block of data returned by GetClassList when the data is no longer needed.

Miscellaneous Account Manager Methods

Besides getting lists of the account managers accounts, categories, etc, you may want to find out a bit more about the account manager you are connecting to. FPI provides the following:

UInt32		CreatorID() const;
const char* AppName() const; // The pretty name
const char* ShortName() const; // 6 characters or less
const char* AppVersion() const; bool Launch(); bool GetMasterCurrency(char *symbol, char *code=0)

These are all self-explanatory with the exception of GetMasterCurrency. GetMasterCurrency expects the client to pass in pointers to two 6-byte buffers. You can use this to ensure that the base currency of your app matches that of the account manager and provide a warning to the user if it does not.


Copyright © 2001, Quik Sense Software

 

Google
WWW http://www.quiksense.com

Home | Links | Consulting | Privacy Statement | Email us

Copyright © 1999-2006 Quik Sense Software. All rights reserved.