diff --git a/src/engine/Transaction.c b/src/engine/Transaction.c index 8dbfc79f4d..35e2d5ea8a 100644 --- a/src/engine/Transaction.c +++ b/src/engine/Transaction.c @@ -34,6 +34,14 @@ #include "TransactionP.h" #include "util.h" + +/* if the "force_double_entry" flag has a non-zero value, + * then all transactions will be *forced* to balance. + * This will be forced even if it requires a new split + * to be created. + */ +int force_double_entry = 0; + /********************************************************************\ * Because I can't use C++ for this project, doesn't mean that I * * can't pretend too! These functions perform actions on the * @@ -155,7 +163,7 @@ void xaccSplitSetShareAmount (Split *s, double amt) s -> damount = amt; } -void xaccSplitSetAmount (Split *s, double amt) +void xaccSplitSetValue (Split *s, double amt) { MARK_SPLIT(s); /* remember, damount is actually share price */ @@ -268,30 +276,92 @@ xaccFreeTransaction( Transaction *trans ) \********************************************************************/ void -xaccTransRecomputeAmount (Transaction *trans) +xaccSplitRebalance (Split *split) { + Transaction *trans; Split *s; int i = 0; - double amount = 0.0; + double value = 0.0; - s = trans->dest_splits[i]; - while (s) { - amount += s->share_price * s->damount; - i++; - s = trans->dest_splits[i]; - } - /* if there is just one split, then the credited - * and the debited splits should match up. */ - if (1 == i) { + trans = split->parent; + + if (&(trans->source_split) == split) { + /* The indicated split is the source split. + * Pick a destination split (by default, + * the first destination split), and force + * the total on it. + */ + s = trans->dest_splits[0]; - trans -> source_split.damount = - (s->damount); - trans -> source_split.share_price = s->share_price; + if (s) { + /* first, add the source split */ + value = split->share_price * split->damount; + + /* now add in the sum of the destination splits */ + i = 0; + while (s) { + value += s->share_price * s->damount; + i++; + s = trans->dest_splits[i]; + } + + /* subtract the first destination split */ + s = trans->dest_splits[0]; + value -= (s->share_price) * (s->damount); + + /* the new value of the destination split + * will be the result. + */ + s -> damount = - (value / (s->share_price)); + MARK_SPLIT (s); + + } else{ + /* There are no destination splits !! + * Either this is allowed, in which case + * we just blow it off, or its forbidden, + * in which case we force a balacing split + * to be created. + */ + + if (force_double_entry) { + value = split->share_price * split->damount; + + /* malloc a new split, mirror it to the source split */ + s = xaccMallocSplit (); + s->damount = -value; + free (s->memo); + s->memo = strdup (split->memo); + free (s->action); + s->action = strdup (split->action); + + /* insert the new split into the transaction and + * the same account as the source split */ + xaccTransAppendSplit (trans, s); + xaccAccountInsertSplit (split->acc, s); + MARK_SPLIT (s); + } + } } else { - trans -> source_split.damount = -amount; - trans -> source_split.share_price = 1.0; + + /* The indicated split is a destination split. + * Compute grand total of all distination splits, + * and force the source split to blanace. + */ + i = 0; + s = trans->dest_splits[i]; + value = 0.0; + while (s) { + value += s->share_price * s->damount; + i++; + s = trans->dest_splits[i]; + } + + s = &(trans->source_split); + s -> damount = - (value / (s->share_price)); + MARK_SPLIT (s); } - MARK_SPLIT (&(trans->source_split)); + } /********************************************************************\ @@ -343,9 +413,6 @@ xaccTransRemoveSplit (Transaction *trans, Split *split) s = trans->dest_splits[n]; } trans->dest_splits[i] = NULL; - - /* bring dollar amounts into synchrony */ - xaccTransRecomputeAmount (trans); } /********************************************************************\ diff --git a/src/engine/Transaction.h b/src/engine/Transaction.h index 80dd4f69c5..5959a4d28b 100644 --- a/src/engine/Transaction.h +++ b/src/engine/Transaction.h @@ -83,10 +83,33 @@ void xaccTransSetReconcile (Transaction *, char); void xaccTransAppendSplit (Transaction *, Split *); void xaccTransRemoveSplit (Transaction *, Split *); -/* recompute the total transaction value, based - * on the sum of the debit splits that belong to this - * transaction. */ -void xaccTransRecomputeAmount (Transaction *); +/* + * The xaccSplitRebalance() routine is an important routine for + * maintaining and ensuring that double-entries balance properly. + * This routine forces the sum-total of the values of all the + * splits in a transaction to total up to exactly zero. + * + * It is worthwhile to understand the algorithm that this routine + * uses to acheive balance. It goes like this: + * If the indicated split is a destination split, then the + * total value of the destination splits is computed, and the + * value of the source split is adjusted to be minus this amount. + * (the share price of the source split is not changed). + * If the indicated split is the source split, then the value + * of the very first destination split is adjusted so that + * the blanace is zero. If there is not destination split, + * one of two outcomes are possible, depending on whether + * "forced_double_entry" is enabled or disabled. + * (1) if forced-double-entry is disabled, the fact that + * the destination is missing is ignored. + * (2) if force-double-entry is enabled, then a destination + * split that exactly mirrors the ource split is created, + * and credited to the same account as the source split. + * Hopefully, the user will notice this, and reparent the + * destination split properly. + */ + +void xaccSplitRebalance (Split *); /* ------------- gets --------------- */ /* return pointer to the source split */ @@ -115,7 +138,7 @@ void xaccSplitSetAction (Split *, const char *); void xaccSplitSetReconcile (Split *, char); /* The following two functions set the amount on the split */ -void xaccSplitSetAmount (Split *, double); +void xaccSplitSetValue (Split *, double); void xaccSplitSetShareAmount (Split *, double);