QIF patches from Dave Peticolas <peticola@morpheus.cs.ucdavis.edu>

Date: Sun, 17 Oct 1999 18:01:53 -0700


git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@1936 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
Linas Vepstas 1999-10-18 02:54:06 +00:00
parent abf5bed7cd
commit 7cf39fd4e5
7 changed files with 178 additions and 81 deletions

View File

@ -87,12 +87,12 @@ xaccInitAccount (Account * acc)
acc->type = -1;
acc->accInfo = NULL;
acc->accountName = NULL;
acc->accountCode = NULL;
acc->description = NULL;
acc->notes = NULL;
acc->currency = NULL;
acc->security = NULL;
acc->accountName = strdup("");
acc->accountCode = strdup("");
acc->description = strdup("");
acc->notes = strdup("");
acc->currency = strdup("");
acc->security = strdup("");
acc->numSplits = 0;
acc->splits = (Split **) _malloc (sizeof (Split *));
@ -840,6 +840,7 @@ xaccConsolidateTransactions (Account * acc)
{
Split *sa, *sb;
Transaction *ta, *tb;
Timespec ts;
int i,j;
int retval;
@ -859,21 +860,57 @@ xaccConsolidateTransactions (Account * acc)
if (retval) continue;
/* OK, looks like the two splits are a matching pair.
* Blow one of them, and its entire associated transaction, away.
* Blow one of them, and its entire associated transaction, away.
* (We blow away the transaction because not only do the splits
* match, but so do all of thier partner-splits. )
* match, but so do all of their partner-splits.)
*
* But, before we blow it away, we go through each split and
* update the reconciled flag and date of the split in the
* remaining transaction with those in the one being deleted,
* but only if the remaining transaction has an NREC reconciled
* flag. In other words, the two splits match on everything
* but the reconciled flags and dates, so we assume that the
* one which is not NREC is 'more correct'. This is true in
* the case of importing two QIF files with overlapping
* transactions. Each file will have one 'half' of the
* transaction, but the other half will be generated by the
* QIF importing routines, but with default values for the
* reconciled data. When we load the other file, we need to
* replace the generated 'half' with the real one.
*/
xaccTransBeginEdit (tb, 1);
xaccTransDestroy (tb);
xaccTransCommitEdit (tb);
xaccTransBeginEdit (ta, 1);
/* It should be safe to just "break" here, as all splits
* wwith index i or less have been checked already and couldn't
* have been dupes. So index i is still valid, although j is
* not. Note that numSplits changed ...
*/
break;
for (i=xaccTransCountSplits(ta); i>0; i--) {
sa = xaccTransGetSplit (ta, i - 1);
/* If the remaining split is reconciled as something
other than NREC, just leave it alone. */
if (xaccSplitGetReconcile(sa) != NREC)
continue;
/* We get the matching split using the tickee value
generated by xaccTransMatch above. */
sb = xaccTransGetSplit(tb, sa->tickee);
xaccSplitSetReconcile (sa, xaccSplitGetReconcile(sb));
xaccSplitGetDateReconciledTS (sb, &ts);
xaccSplitSetDateReconciledTS (sa, &ts);
}
xaccTransCommitEdit (ta);
xaccTransBeginEdit (tb, 1);
xaccTransDestroy (tb);
xaccTransCommitEdit (tb);
/* It should be safe to just "break" here, as all splits
* with index i or less have been checked already and couldn't
* have been dupes. So index i is still valid, although j is
* not. Note that numSplits changed ...
*/
break;
}
}
}

View File

@ -644,17 +644,21 @@ void
xaccConcatGroups (AccountGroup *togrp, AccountGroup *fromgrp)
{
Account * acc;
int numAcc;
if (!togrp) return;
if (!fromgrp) return;
/* the act of inserting the account into togrp also causes
* it to automatically be deleted from fromgrp. So just loop
* until they're all gone.
/* The act of inserting the account into togrp also causes
* it to automatically be deleted from fromgrp. But use a
* saved copy of fromgrp's numAcc member since, after the
* last insertion, fromgrp will be pointing to freed memory.
*/
while (fromgrp->numAcc) {
numAcc = fromgrp->numAcc;
while (numAcc) {
acc = fromgrp->account[0];
xaccGroupInsertAccount (togrp, acc);
numAcc--;
}
}
@ -665,7 +669,7 @@ void
xaccMergeAccounts (AccountGroup *grp)
{
Account *acc_a, *acc_b;
int i,j, k;
int i, j, k;
if (!grp) return;
@ -694,7 +698,8 @@ xaccMergeAccounts (AccountGroup *grp)
} else {
xaccConcatGroups (ga, gb);
acc_b->children = NULL;
xaccFreeAccountGroup (gb);
/* XXX why is the below commented out ??? */
/* xaccFreeAccountGroup (gb); XXX ??? */
}
}

View File

@ -172,14 +172,10 @@ char * xaccReadQIFCategory (int fd, Account * acc)
char * qifline;
char * tmp;
if (!acc) return NULL;
xaccAccountBeginEdit (acc, 0);
xaccAccountSetType (acc, -1);
xaccAccountSetName (acc, "");
xaccAccountSetDescription (acc, "");
xaccAccountSetNotes (acc, "");
qifline = xaccReadQIFLine (fd);
if (!qifline) return NULL;
@ -269,9 +265,6 @@ char * xaccReadQIFAccount (int fd, Account * acc)
xaccAccountBeginEdit (acc, 0);
xaccAccountSetType (acc, -1);
xaccAccountSetName (acc, "");
xaccAccountSetDescription (acc, "");
xaccAccountSetNotes (acc, "");
qifline = xaccReadQIFLine (fd);
if (!qifline) return NULL;
@ -518,8 +511,6 @@ GetSubQIFAccount (AccountGroup *rootgrp, char *qifline, int acc_type)
if (!xfer_acc) {
xfer_acc = xaccMallocAccount ();
xaccAccountSetName (xfer_acc, qifline);
xaccAccountSetDescription (xfer_acc, "");
xaccAccountSetNotes (xfer_acc, "");
xaccAccountSetCurrency (xfer_acc, gnc_qif_import_currency);
if (0 > acc_type) acc_type = GuessAccountType (qifline);
@ -604,8 +595,6 @@ xaccGetSecurityQIFAccount (Account *acc, char *qifline)
if (!xfer_acc) {
xfer_acc = xaccMallocAccount ();
xaccAccountSetName (xfer_acc, qifline);
xaccAccountSetDescription (xfer_acc, "");
xaccAccountSetNotes (xfer_acc, "");
xaccAccountSetCurrency (xfer_acc, gnc_qif_import_currency);
xaccAccountSetType (xfer_acc, STOCK);
@ -624,6 +613,10 @@ xaccGetSecurityQIFAccount (Account *acc, char *qifline)
* *
* Args: fd -- file descriptor *
* Args: acc -- account structure to fill in *
* Args: guess_name -- true if we should try and guess the name *
* based on an opening balance entry *
* Args: first_trans -- true if this is the first transaction to *
* be processed in this account *
* Return: first new line after end of transaction *
\********************************************************************/
@ -634,13 +627,15 @@ xaccGetSecurityQIFAccount (Account *acc, char *qifline)
char *
xaccReadQIFTransaction (int fd, Account *acc, int *name_not_yet_set)
xaccReadQIFTransaction (int fd, Account *acc, int guess_name,
int first_trans)
{
Transaction *trans;
Split *source_split;
Split *split = NULL;
char * qifline;
char * tmp;
int opening_balance = 0;
int isneg = 0;
int got_share_quantity = 0;
int share_xfer = 0;
@ -681,6 +676,7 @@ xaccReadQIFTransaction (int fd, Account *acc, int *name_not_yet_set)
time_t secs;
secs = xaccParseQIFDate (&qifline[1]);
xaccTransSetDateSecs (trans, secs);
xaccTransSetDateEnteredSecs (trans, secs);
}
break;
case 'E': /* E == memo for split */
@ -697,25 +693,27 @@ xaccReadQIFTransaction (int fd, Account *acc, int *name_not_yet_set)
break;
case 'L': /* L == name of acount from which transfer occured */
/* MSMoney uses a cute trick to overcome the lack of an account name
* in the QIF format. Basically, if the very very first transaction
* has a payee field of "Opening Balance", then the L field is the name
* of this account, and not the transfer account. But this only works
* for the very, very first transaction.
*/
if (*name_not_yet_set) {
*name_not_yet_set = 0;
/* remove square brackets from name, remove carriage return ... */
qifline = &qifline[1];
if ('[' == qifline[0]) {
* in the QIF format. Basically, if the very very first transaction
* has a payee field of "Opening Balance", then the L field is the name
* of this account, and not the transfer account. But this only works
* for the very, very first transaction. This also seems to be the case
* for Quicken 5.0 (and others?).
*/
if (opening_balance) {
if (guess_name) {
/* remove square brackets from name, remove carriage return ... */
qifline = &qifline[1];
tmp = strchr (qifline, ']');
if ('[' == qifline[0]) {
qifline = &qifline[1];
tmp = strchr (qifline, ']');
if (tmp) *tmp = 0x0;
}
tmp = strchr (qifline, '\r');
if (tmp) *tmp = 0x0;
tmp = strchr (qifline, '\n');
if (tmp) *tmp = 0x0;
xaccAccountSetName (acc, qifline);
}
tmp = strchr (qifline, '\r');
if(tmp) *tmp = 0x0;
tmp = strchr (qifline, '\n');
if(tmp) *tmp = 0x0;
xaccAccountSetName (acc, qifline);
} else {
/* locate the transfer account */
xfer_acc = xaccGetXferQIFAccount (acc, qifline);
@ -770,11 +768,12 @@ xaccReadQIFTransaction (int fd, Account *acc, int *name_not_yet_set)
* in the QIF format. Basically, if the very very first transaction
* has a payee field of "Opening Balance", then the L field is the name
* of this account, and not the transfer account. But this only works
* for the very, very first transaction.
* for the very, very first transaction. This also seems to be the case
* for Quicken 5.0 (and others?).
*/
if (*name_not_yet_set) {
if (! NSTRNCMP (qifline, "POpening Balance")) *name_not_yet_set = 0;
}
if (first_trans)
if (NSTRNCMP (qifline, "POpening Balance"))
opening_balance = GNC_T;
break;
case 'Q':
@ -940,15 +939,15 @@ xaccReadQIFTransaction (int fd, Account *acc, int *name_not_yet_set)
* the indicated account
\********************************************************************/
char * xaccReadQIFTransList (int fd, Account *acc, int *acc_name_not_yet_set)
char * xaccReadQIFTransList (int fd, Account *acc, int guess_name)
{
char * qifline;
if (!acc) return 0x0;
qifline = xaccReadQIFTransaction (fd, acc, acc_name_not_yet_set);
qifline = xaccReadQIFTransaction (fd, acc, guess_name, GNC_T);
while (qifline) {
if ('!' == qifline[0]) break;
qifline = xaccReadQIFTransaction (fd, acc, acc_name_not_yet_set);
qifline = xaccReadQIFTransaction (fd, acc, guess_name, GNC_F);
}
return qifline;
}
@ -958,7 +957,7 @@ char * xaccReadQIFTransList (int fd, Account *acc, int *acc_name_not_yet_set)
\********************************************************************/
/********************************************************************\
* xaccReadQIFAccountGroup *
* xaccReadQIFAccountGroup *
* reads in the data from file datafile *
* *
* Args: datafile - the file to load the data from *
@ -1025,14 +1024,13 @@ xaccReadQIFAccountGroup( char *datafile )
}
if (name) {
int bogus_acc_name = 1;
Account * acc = xaccMallocAccount();
xaccAccountSetType (acc, typo);
xaccAccountSetName (acc, name);
xaccAccountSetCurrency (acc, gnc_qif_import_currency);
xaccGroupInsertAccount( grp, acc );
qifline = xaccReadQIFTransList (fd, acc, &bogus_acc_name);
qifline = xaccReadQIFTransList (fd, acc, GNC_T);
typo = -1; name = NULL;
continue;
} else
@ -1083,8 +1081,7 @@ xaccReadQIFAccountGroup( char *datafile )
/* read account name, followed by dollar data ... */
char * acc_name;
Account *preexisting;
Account *acc = xaccMallocAccount();
int guess_acc_name = 0;
Account *acc = xaccMallocAccount();
DEBUG ("got account\n");
xaccAccountSetCurrency (acc, gnc_qif_import_currency);
@ -1099,7 +1096,7 @@ xaccReadQIFAccountGroup( char *datafile )
acc_name = xaccAccountGetName (acc);
preexisting = xaccGetAccountFromName (grp, acc_name);
if (preexisting)
{
{
xaccFreeAccount (acc);
acc = preexisting;
}
@ -1122,7 +1119,7 @@ xaccReadQIFAccountGroup( char *datafile )
/* read transactions */
/* note, we have a real account name, so no need to go guessing it. */
if (qifline) qifline = xaccReadQIFTransList (fd, acc, &guess_acc_name);
if (qifline) qifline = xaccReadQIFTransList (fd, acc, GNC_F);
}
continue;
} else

View File

@ -1339,7 +1339,7 @@ xaccSplitOrder (Split **sa, Split **sb)
SAFE_STRCMP (da, db);
/* the reconciled flag ... */
diff = ((*sa)->reconciled) - ((*sb)->reconciled) ;
diff = ((*sa)->reconciled) - ((*sb)->reconciled);
if (diff) return diff;
/* if dates differ, return */
@ -1353,6 +1353,53 @@ xaccSplitOrder (Split **sa, Split **sb)
return 0;
}
int
xaccSplitMatch (Split **sa, Split **sb)
{
char *da, *db;
char diff;
if ( (*sa) && !(*sb) ) return -1;
if ( !(*sa) && (*sb) ) return +1;
if ( !(*sa) && !(*sb) ) return 0;
/* compare amounts use parenthesis paranoia for multiplication, pointers etc. */
if ( ((((*sa)->damount)*((*sa)->share_price))+EPS) <
(((*sb)->damount)*((*sb)->share_price))) return -1;
if ( ((((*sa)->damount)*((*sa)->share_price))-EPS) >
(((*sb)->damount)*((*sb)->share_price))) return +1;
if ((((*sa)->share_price)+EPS) < ((*sb)->share_price)) return -1;
if ((((*sa)->share_price)-EPS) > ((*sb)->share_price)) return +1;
/* otherwise, sort on memo strings */
da = (*sa)->memo;
db = (*sb)->memo;
SAFE_STRCMP (da, db);
/* otherwise, sort on action strings */
da = (*sa)->action;
db = (*sb)->action;
SAFE_STRCMP (da, db);
/* If the reconciled flags are different, don't compare the
* dates, since we want to match splits with different reconciled
* values. But if they do match, the dates must match as well.
* Note that
*/
diff = ((*sa)->reconciled) - ((*sb)->reconciled);
if (!diff) {
DATE_CMP(sa,sb,date_reconciled);
}
/* otherwise, sort on docref string */
da = (*sa)->docref;
db = (*sb)->docref;
SAFE_STRCMP (da, db);
return 0;
}
int
xaccTransOrder (Transaction **ta, Transaction **tb)
@ -1420,7 +1467,7 @@ xaccTransMatch (Transaction **tap, Transaction **tbp)
nb=0;
while ((sb=tb->splits[nb])) {
if (-1 < sb->tickee) {nb++; continue;}
retval = xaccSplitOrder (&sa, &sb);
retval = xaccSplitMatch (&sa, &sb);
if ((0 == retval) && (sa->acc = sb->acc)) {
sb->tickee = na;
sa->tickee = nb;

View File

@ -44,7 +44,7 @@
*/
/* A split transaction is one which shows up as a credit (or debit) in
* one account, and peices of it show up as debits (or credits) in other
* one account, and pieces of it show up as debits (or credits) in other
* accounts. Thus, a single credit-card transaction might be split
* between "dining", "tips" and "taxes" categories.
*/
@ -94,18 +94,18 @@ void xaccConfigSetForceDoubleEntry (int force);
int xaccConfigGetForceDoubleEntry (void);
/*
* The xaccMallocTransaction() will malloc memory and initilize it.
* The xaccMallocTransaction() will malloc memory and initialize it.
* Once created, it is usually unsafe to merely "free" this memory;
* the xaccTransDestroy() method should be called.
*
* The xaccInitTransaction() method will initialize the indicated memory
* area. Handy for on-stack trasnactions.
* area. Handy for on-stack transactions.
*/
Transaction * xaccMallocTransaction (void);
void xaccInitTransaction (Transaction *);
/* The xaccTransDestroy() method will remove all
* of the splits from each of thier accounts, free the memory
* of the splits from each of their accounts, free the memory
* associated with them. This routine must be followed by either
* an xaccTransCommitEdit(), in which case the transaction
* memory will be freed, or by xaccTransRollbackEdit(), in which
@ -115,7 +115,7 @@ void xaccInitTransaction (Transaction *);
void xaccTransDestroy (Transaction *);
/* The xaccTransBeginEdit() method must be called before any changes
* are made to a transaction or any of its componenet splits. If
* are made to a transaction or any of its component splits. If
* this is not done, errors will result. If the defer flag is set,
* then the automated re-balancing of all splits in this transaction
* is defered until the xaccTransCommitEdit() call. This allows
@ -123,8 +123,8 @@ void xaccTransDestroy (Transaction *);
* system sent temporarily out of balance, up until the Commit
* call is made when double-entry is once again enforced.
*
* The xaccTransCommitEdit() method should be used toindicate that
* all of the manipulations on teh transaction are complete, and
* The xaccTransCommitEdit() method should be used to indicate that
* all of the manipulations on the transaction are complete, and
* that these should be made permanent. Note that this routine
* may result in the deletion of the transaction, if the transaction
* is "empty" (has no splits, or * has a single split in it whose
@ -148,7 +148,7 @@ Timespec gnc_dmy2timespec(int day, int month, int year);
* of the transaction. (Footnote: this shouldn't matter to a user,
* but anyone modifying the engine should understand that when
* xaccTransCommitEdit() is called, the date order of each of the
* component splits will be checked, and will be resotred in
* component splits will be checked, and will be restored in
* ascending date order.)
*
* The xaccTransSetDate() method does the same thing as
@ -176,7 +176,7 @@ void xaccTransSetDescription (Transaction *, const char *);
/* The xaccTransSetMemo() and xaccTransSetAction() methods are
* convenience routines to keep the memo and action fields
* of two-split trasnactions in sync. If the transaction has
* of two-split transactions in sync. If the transaction has
* precisely two splits, then these routines will set the memo
* and action of both splits. Otherwise, they will set the
* memo and action of the first split (source split) only.
@ -402,6 +402,16 @@ Account * xaccSplitGetAccount (Split *);
* Finally, it returns zero if all of the above match.
* Note that it does *NOT* compare its parent transaction.
*
* The xaccSplitMatch(sa,sb) method works like the xaccSplitOrder method
* except for the reconciled flag and reconciled dates. If the two
* splits have different reconciled flags, then neither the reconciled
* flags nor the reconciled dates are used in the comparison. If the
* reconciled flags are the same, the reconciled dates are compared
* as in xaccSplitOrder. This method is useful for matching splits
* which are "almost" the same, as would be generated by loading
* two QIF files for two different accounts that have transactions
* in common.
*
* The xaccSplitDateOrder(sa,sb) method is useful for sorting.
* It is just like the xaccSplitOrder() routine, except that it first
* calls xaccTransOrder(), and then does split comparisons only if
@ -410,13 +420,14 @@ Account * xaccSplitGetAccount (Split *);
* The xaccTransMatch() method is just like the xaccTransOrder method,
* except that it also performs a comparison of each of its child splits,
* and returns a zero (match) condition only if they match as well.
* Note that split-matching includes matching thier parent accounts.
* Note that split-matching includes matching their parent accounts.
*/
int xaccTransOrder (Transaction **ta, Transaction **tb);
int xaccTransMatch (Transaction **ta, Transaction **tb);
int xaccSplitOrder (Split **sa, Split **sb);
int xaccSplitDateOrder (Split **sa, Split **sb);
int xaccSplitMatch (Split **sa, Split **sb);
/********************************************************************\
* Miscellaneous utility routines.
@ -452,7 +463,7 @@ int xaccIsPeerSplit (Split *, Split *);
/* The IthSplit() and IthTransaction() routines merely dereference
* the lists supplied as arguments; i.e. they return list[i].
* These routines are needed by the perl swig wrappers, which
* are unable to dereference on thier own.
* are unable to dereference on their own.
*/
Transaction * IthTransaction (Transaction **tarray, int i);
Split * IthSplit (Split **sarray, int i);

View File

@ -75,13 +75,13 @@ struct _split
Transaction *parent; /* parent of split */
/* The memo field is an arbitrary user-assiged value.
* It is intended to hold a short (zero to forty cahracter) string
* It is intended to hold a short (zero to forty character) string
* that is displayed by the GUI along with this split.
*/
char * memo;
/* The action field is an arbitrary user-assigned value.
* It is meant to be a very short (oen to ten cahracter) string that
* It is meant to be a very short (one to ten cahracter) string that
* signifies the "type" of this split, such as e.g. Buy, Sell, Div,
* Withdraw, Deposit, ATM, Check, etc. The idea is that this field
* can be used to create custom reports or graphs of data.

View File

@ -88,7 +88,7 @@ extern int loglevel[MODULE_MAX];
#define ERROR() fprintf(stderr,"%s: Line %d, error = %s\n", \
__FILE__, __LINE__, strerror(errno));
#ifdef DEBUGMEMORY
#if DEBUG_MEMORY
void *dmalloc( size_t size );
void dfree( void *ptr );
size_t dcoresize();