diff --git a/src/engine/Account.c b/src/engine/Account.c index f3f9f0cb6f..be382d0021 100644 --- a/src/engine/Account.c +++ b/src/engine/Account.c @@ -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); + + 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; - /* 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; + /* 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; } } } diff --git a/src/engine/Group.c b/src/engine/Group.c index a48fcb132a..e3f0a5bd6f 100644 --- a/src/engine/Group.c +++ b/src/engine/Group.c @@ -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 ??? */ } } diff --git a/src/engine/QIFIO.c b/src/engine/QIFIO.c index c549552389..44fd03afa6 100644 --- a/src/engine/QIFIO.c +++ b/src/engine/QIFIO.c @@ -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 diff --git a/src/engine/Transaction.c b/src/engine/Transaction.c index 554599db2b..70c384f1c7 100644 --- a/src/engine/Transaction.c +++ b/src/engine/Transaction.c @@ -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; diff --git a/src/engine/Transaction.h b/src/engine/Transaction.h index 5e62947a21..08f596fa8a 100644 --- a/src/engine/Transaction.h +++ b/src/engine/Transaction.h @@ -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); diff --git a/src/engine/TransactionP.h b/src/engine/TransactionP.h index 651b8f6468..a0510a94ed 100644 --- a/src/engine/TransactionP.h +++ b/src/engine/TransactionP.h @@ -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. diff --git a/src/engine/util.h b/src/engine/util.h index a3307afed4..2f44f5d6e3 100644 --- a/src/engine/util.h +++ b/src/engine/util.h @@ -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();