mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
* src/doc/Makefile.am:
* src/doc/qif.txt: Add new qif importer documentation to the repository/dist git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@9761 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
parent
371c40076c
commit
d5f15e0cbe
@ -3,6 +3,10 @@
|
||||
* src/engine/Transaction.h: fix the xaccTransOrder() documentation
|
||||
to be more accurate with the actual implementation.
|
||||
|
||||
* src/doc/Makefile.am:
|
||||
* src/doc/qif.txt:
|
||||
Add new qif importer documentation to the repository/dist
|
||||
|
||||
2004-01-06 Derek Atkins <derek@ihtfp.com>
|
||||
|
||||
* src/engine/qofinstance.c: revert fix from 01-01, because it's wrong.
|
||||
|
@ -27,6 +27,7 @@ EXTRA_DIST = \
|
||||
netlogin.txt \
|
||||
query-api.txt \
|
||||
guid.txt \
|
||||
qif.txt \
|
||||
user-prefs-howto.txt
|
||||
|
||||
|
||||
|
194
src/doc/qif.txt
Normal file
194
src/doc/qif.txt
Normal file
@ -0,0 +1,194 @@
|
||||
The (new new) QIF Importer infrastructure
|
||||
Derek Atkins <derek@ihtfp.com>
|
||||
2004-01-07
|
||||
|
||||
A work in progress....
|
||||
|
||||
0. Introduction
|
||||
|
||||
The existing qif importer in src/import-export/qif-import is both hard
|
||||
to maintain and hard to re-integrate into the shared import
|
||||
architecture. Similarly, the half-completed re-write in qif-io-core
|
||||
is similarly hard to maintain (although it is arguably easier to
|
||||
integrate). One problem with both of these solutions is that they are
|
||||
written in Scheme, a language that many gnucash developers just don't
|
||||
understand well. Another issue is that the code is not commented and
|
||||
no documentation exists to help future developers track down bugs or
|
||||
extend the importer as QIF changes over time (c.f. Memorized
|
||||
Transaction import).
|
||||
|
||||
As much as "complete rewrite" tends to be a lot of work for little
|
||||
gain, when few (if any) developers can understand the implementation
|
||||
well enough to make changes, a complete re-write may make sense. This
|
||||
document is an attempt to describe the architecture of the new
|
||||
importer, implemented in C, and how it interfaces to the rest of the
|
||||
import infrastructure.
|
||||
|
||||
|
||||
1. Importer Architecture
|
||||
|
||||
The importer is a multi-step, staged system that should implement a
|
||||
read, parse, convert, combine, filter, finish process. The importer
|
||||
starts with a clean import context and then each processing step
|
||||
modifies it as per the user's requirements. A small set of APIs allow
|
||||
the user to progress along the processing steps (and an internal state
|
||||
machine makes sure the caller proceeds in the proper order).
|
||||
|
||||
The importer is driven by the UI code; the importer itself is just a
|
||||
multi-stage worker. The UI code calls each step in the process. For
|
||||
long-running operations the UI can provide a callback mechanism for a
|
||||
progress bar of completion.
|
||||
|
||||
Each stage of the import process may require some user input. What
|
||||
input is required depends on the stage of the process and what the
|
||||
last stage returned. In some cases stages can be skipped. For
|
||||
example, during the conversion phase if the date format is unambigious
|
||||
then no user input would be required and the "ask for date format
|
||||
disamiguation" input can be skipped.
|
||||
|
||||
QUESTION: How does the importer relate the processing state back to
|
||||
the UI? Simiarly, how does it pass back specific disambiguating
|
||||
questions to ask the user (and how are those responses returned to the
|
||||
importer)?
|
||||
|
||||
|
||||
2. The Import Process
|
||||
|
||||
The import process starts when the UI creates a new import context.
|
||||
All of a single import is performed within that context. The context
|
||||
model allows multiple import processes to take place simultaneously.
|
||||
|
||||
The first step in the import process is selecting the file (or files)
|
||||
to be imported. The UI passes each filename to the importer which
|
||||
reads the file and performs a quick parse process to break the file
|
||||
down into its component QIF parts. While the importer should allow
|
||||
the user to iteratively add more and more files to the import context,
|
||||
it should also allow the user to select multiple files at once
|
||||
(e.g. *.qif) to reduce the user workload.
|
||||
|
||||
Each imported file may be a complete QIF file or it may be a single
|
||||
QIF account file. In the latter case the UI needs to ask the user for
|
||||
the actual QIF account name for the file. Similarly, each file may
|
||||
need user intervention to disambiguate various data, like the date or
|
||||
number formats.
|
||||
|
||||
QUESTION: If the user provides multiple files at once and each file
|
||||
has internal ambiguities (e.g. the date format), should the user be
|
||||
asked for each file, or can we assume that all the files have the same
|
||||
format? Perhaps the UI should allow the user to "make this choice for
|
||||
all files"?
|
||||
|
||||
Once the user chooses all their files (they can also remove files
|
||||
during the process) the importer will combine the files into a common
|
||||
import, trying to match QIF accounts and transactions from different
|
||||
files. Part of this is duplicate detection, because QIF only includes
|
||||
half a transaction (for any QIF transaction you only know the local
|
||||
account, not necessarily the "far" account). If the importer sees
|
||||
multiple parts of the same transaction it can (and should) combine
|
||||
them into a single transaction, thereby pinning down the near and far
|
||||
accounts.
|
||||
|
||||
The next series of steps maps QIF data objects to GnuCash data
|
||||
objects. In particular, the importer needs the help of the UI to map
|
||||
unknown QIF Accounts and Categories to GnuCash Accounts (the latter to
|
||||
Income and Expense Accounts) and QIF Securities to GnuCash
|
||||
Commodities. Finally the importer can use the generic transaction
|
||||
matcher to map the existing transactions to potential duplicates and
|
||||
also Payee/Memo fields to "destination accounts".
|
||||
|
||||
At the end of this process the accounts, commodities, and transactions
|
||||
are merged into the existing account tree, and the import context is
|
||||
freed.
|
||||
|
||||
|
||||
3. Importer Data Objects
|
||||
|
||||
QifContext
|
||||
QifError
|
||||
QifFile
|
||||
QifObject
|
||||
+-QifAccount
|
||||
+-QifCategory
|
||||
+-QifClass
|
||||
+-QifSecurity
|
||||
+-QifTxn
|
||||
+-QifInvstTxn
|
||||
|
||||
Internal Data Types
|
||||
|
||||
QifHandler
|
||||
QifData
|
||||
|
||||
4. Importer API
|
||||
|
||||
QIF Contexts
|
||||
|
||||
/** Create and destroy an import context */
|
||||
QifContext qif_context_create(void)
|
||||
void qif_context_destroy(QifContext ctx)
|
||||
|
||||
/** return the list of QifFiles in the context. */
|
||||
GList *qif_context_get_files(QifContext ctx)
|
||||
|
||||
/** merge all the files in the context up into the context, finding
|
||||
* matched accounts and transactions, so everything is working off the
|
||||
* same set of objects within the context.
|
||||
*/
|
||||
void qif_context_merge_files(QifContext ctx);
|
||||
|
||||
|
||||
QIF Files
|
||||
|
||||
/**
|
||||
* Open, read, and minimally parse the QIF file, filename.
|
||||
* If progress is non-NULL, will call progress with pg_arg and a value from
|
||||
* 0.0 to 1.0 that indicates the percentage of the file read and parsed.
|
||||
* Returns the new QifFile or NULL if there was some failure during the process.
|
||||
*/
|
||||
QifFile qif_file_new(QifContext ctx, const char* filename,
|
||||
void(*progress)(gpointer, double), gpointer pg_arg)
|
||||
|
||||
/** removes file from the list of active files in the import context. */
|
||||
void qif_file_remove(QifContext ctx, QifFile file)
|
||||
|
||||
/** Return the filename of the QIF file */
|
||||
const char * qif_file_filename(QifFile file);
|
||||
|
||||
/** Does a file need a default QIF account? */
|
||||
gboolean qif_file_needs_account(QifFile file);
|
||||
|
||||
/** Provide a default QIF Account-name for the QIF File */
|
||||
void qif_file_set_default_account(QifFile file, const char *acct_name);
|
||||
|
||||
/** Parse the qif file values; may require some callbacks to let the
|
||||
* user choose from ambigious data formats
|
||||
* XXX: Is there a better way to callback from here?
|
||||
* Do we need progress-bar info here?
|
||||
*/
|
||||
QifError qif_file_parse(QifFile ctx, gpointer ui_arg)
|
||||
|
||||
|
||||
5. Importer State Machine
|
||||
|
||||
The state machine has the following structure. Named states (and substates)
|
||||
must proceed in order. Some states (e.g. state b) have multiple choices.
|
||||
For example you could enter substates b1-b2, or you can run qif_file_remove.
|
||||
|
||||
a. Create the context
|
||||
- qif_context_create
|
||||
b. Add/Remove files to be imported
|
||||
b1. Add file
|
||||
- qif_file_new
|
||||
b2. Parse the added file
|
||||
- qif_file_parse
|
||||
Note that this needs to callback into the ui to handle ambiguities
|
||||
- qif_file_remove
|
||||
If the user wants to remove some files from the import context
|
||||
- repeat (b) as necessary until user choses to move to (c)
|
||||
c. Once all files are chosen, merge internally and continue the process
|
||||
- qif_context_merge_files
|
||||
d. map qif accounts to gnucash accounts
|
||||
e. map qif categories to gnucash accounts
|
||||
f. map qif securities to gnucash commodities
|
||||
g. duplicate detection with existing gnucash txns
|
||||
h. transaction matcher (map one-sided txns using Payee/Memo info)
|
Loading…
Reference in New Issue
Block a user