mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
redesign for atomic operations
git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@773 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
parent
cb34c2bc00
commit
30837b6bb7
@ -23,6 +23,7 @@
|
||||
* Huntington Beach, CA 92648-4632 *
|
||||
\********************************************************************/
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "config.h"
|
||||
@ -94,20 +95,8 @@ xaccMallocSplit( void )
|
||||
void
|
||||
xaccFreeSplit( Split *split )
|
||||
{
|
||||
Transaction *trans;
|
||||
|
||||
if (!split) return;
|
||||
|
||||
/* free a split only if it is not claimed
|
||||
* by any accounts. */
|
||||
if (split->acc) return;
|
||||
|
||||
/* free the split only if its not a source split */
|
||||
trans = split->parent;
|
||||
if (&(trans->source_split) == split) return;
|
||||
|
||||
xaccTransRemoveSplit (trans, split);
|
||||
|
||||
free(split->memo);
|
||||
free(split->action);
|
||||
|
||||
@ -132,12 +121,10 @@ xaccFreeSplit( Split *split )
|
||||
static void
|
||||
MarkChanged (Transaction *trans)
|
||||
{
|
||||
MARK_SPLIT (&(trans->source_split));
|
||||
|
||||
if (trans->dest_splits) {
|
||||
if (trans->splits) {
|
||||
int i=0;
|
||||
while (trans->dest_splits[i]) {
|
||||
MARK_SPLIT (trans->dest_splits[i]);
|
||||
while (trans->splits[i]) {
|
||||
MARK_SPLIT (trans->splits[i]);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
@ -244,11 +231,8 @@ xaccInitTransaction( Transaction * trans )
|
||||
trans->num = strdup("");
|
||||
trans->description = strdup("");
|
||||
|
||||
trans->dest_splits = (Split **) _malloc (sizeof (Split *));
|
||||
trans->dest_splits[0] = NULL;
|
||||
|
||||
xaccInitSplit ( &(trans->source_split));
|
||||
trans->source_split.parent = trans;
|
||||
trans->splits = (Split **) _malloc (sizeof (Split *));
|
||||
trans->splits[0] = NULL;
|
||||
|
||||
/* create at least one destination split */
|
||||
dsplit = xaccMallocSplit ();
|
||||
@ -284,38 +268,25 @@ xaccFreeTransaction( Transaction *trans )
|
||||
|
||||
/* free a transaction only if it is not claimed
|
||||
* by any accounts. */
|
||||
if (trans->source_split.acc) return;
|
||||
|
||||
i = 0;
|
||||
s = trans->dest_splits[i];
|
||||
s = trans->splits[i];
|
||||
while (s) {
|
||||
if (s->acc) return;
|
||||
i++;
|
||||
s = trans->dest_splits[i];
|
||||
s = trans->splits[i];
|
||||
}
|
||||
|
||||
/* free up the destination splits */
|
||||
i = 0;
|
||||
s = trans->dest_splits[i];
|
||||
s = trans->splits[i];
|
||||
while (s) {
|
||||
xaccFreeSplit (s);
|
||||
i++;
|
||||
s = trans->dest_splits[i];
|
||||
s = trans->splits[i];
|
||||
}
|
||||
|
||||
_free (trans->dest_splits);
|
||||
|
||||
|
||||
/* free up the source-split related stuff. */
|
||||
free(trans->source_split.memo);
|
||||
free(trans->source_split.action);
|
||||
|
||||
/* just in case someone looks up freed memory ... */
|
||||
trans->source_split.memo = 0x0;
|
||||
trans->source_split.reconciled = NREC;
|
||||
trans->source_split.damount = 0.0;
|
||||
trans->source_split.share_price = 1.0;
|
||||
trans->source_split.parent = NULL;
|
||||
_free (trans->splits);
|
||||
|
||||
/* free up transaction strings */
|
||||
free(trans->num);
|
||||
@ -357,7 +328,7 @@ xaccTransCommitEdit (Transaction *trans)
|
||||
void
|
||||
xaccTransRebalance (Transaction * trans)
|
||||
{
|
||||
xaccSplitRebalance (&(trans->source_split));
|
||||
xaccSplitRebalance (trans->splits[0]);
|
||||
}
|
||||
|
||||
void
|
||||
@ -368,31 +339,33 @@ xaccSplitRebalance (Split *split)
|
||||
int i = 0;
|
||||
double value = 0.0;
|
||||
|
||||
|
||||
trans = split->parent;
|
||||
assert (trans);
|
||||
assert (trans->splits);
|
||||
assert (trans->splits[0]);
|
||||
|
||||
if (&(trans->source_split) == split) {
|
||||
if (split == trans->splits[0]) {
|
||||
/* 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];
|
||||
s = trans->splits[1];
|
||||
if (s) {
|
||||
/* first, add the source split */
|
||||
value = split->share_price * split->damount;
|
||||
|
||||
/* now add in the sum of the destination splits */
|
||||
i = 0;
|
||||
i = 1;
|
||||
while (s) {
|
||||
value += s->share_price * s->damount;
|
||||
i++;
|
||||
s = trans->dest_splits[i];
|
||||
s = trans->splits[i];
|
||||
}
|
||||
|
||||
/* subtract the first destination split */
|
||||
s = trans->dest_splits[0];
|
||||
s = trans->splits[1];
|
||||
value -= (s->share_price) * (s->damount);
|
||||
|
||||
/* the new value of the destination split
|
||||
@ -409,6 +382,8 @@ xaccSplitRebalance (Split *split)
|
||||
* to be created.
|
||||
*/
|
||||
|
||||
/* hack alert -- I think this is broken */
|
||||
#ifdef HACK_ALERT
|
||||
if (force_double_entry) {
|
||||
value = split->share_price * split->damount;
|
||||
|
||||
@ -426,23 +401,25 @@ xaccSplitRebalance (Split *split)
|
||||
xaccAccountInsertSplit (split->acc, s);
|
||||
MARK_SPLIT (s);
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
} else {
|
||||
|
||||
/* The indicated split is a destination split.
|
||||
* Compute grand total of all distination splits,
|
||||
* Compute grand total of all destination splits,
|
||||
* and force the source split to blanace.
|
||||
*/
|
||||
i = 0;
|
||||
s = trans->dest_splits[i];
|
||||
i = 1;
|
||||
s = trans->splits[i];
|
||||
value = 0.0;
|
||||
while (s) {
|
||||
value += s->share_price * s->damount;
|
||||
i++;
|
||||
s = trans->dest_splits[i];
|
||||
s = trans->splits[i];
|
||||
}
|
||||
|
||||
s = &(trans->source_split);
|
||||
s = trans->splits[0];
|
||||
s -> damount = - (value / (s->share_price));
|
||||
MARK_SPLIT (s);
|
||||
}
|
||||
@ -470,15 +447,15 @@ xaccTransAppendSplit (Transaction *trans, Split *split)
|
||||
|
||||
/* first, insert the split into the array */
|
||||
split->parent = trans;
|
||||
num = xaccCountSplits (trans->dest_splits);
|
||||
num = xaccCountSplits (trans->splits);
|
||||
|
||||
oldarray = trans->dest_splits;
|
||||
trans->dest_splits = (Split **) _malloc ((num+2)*sizeof(Split *));
|
||||
oldarray = trans->splits;
|
||||
trans->splits = (Split **) _malloc ((num+2)*sizeof(Split *));
|
||||
for (i=0; i<num; i++) {
|
||||
(trans->dest_splits)[i] = oldarray[i];
|
||||
(trans->splits)[i] = oldarray[i];
|
||||
}
|
||||
trans->dest_splits[num] = split;
|
||||
trans->dest_splits[num+1] = NULL;
|
||||
trans->splits[num] = split;
|
||||
trans->splits[num+1] = NULL;
|
||||
|
||||
if (oldarray) _free (oldarray);
|
||||
|
||||
@ -499,15 +476,15 @@ xaccTransRemoveSplit (Transaction *trans, Split *split)
|
||||
if (!trans) return;
|
||||
split->parent = NULL;
|
||||
|
||||
s = trans->dest_splits[0];
|
||||
s = trans->splits[0];
|
||||
while (s) {
|
||||
trans->dest_splits[i] = trans->dest_splits[n];
|
||||
trans->splits[i] = trans->splits[n];
|
||||
if (split == s) { i--; }
|
||||
i++;
|
||||
n++;
|
||||
s = trans->dest_splits[n];
|
||||
s = trans->splits[n];
|
||||
}
|
||||
trans->dest_splits[i] = NULL;
|
||||
trans->splits[i] = NULL;
|
||||
|
||||
/* force double entry to always be consistent */
|
||||
xaccTransRebalance (trans);
|
||||
@ -659,6 +636,7 @@ xaccTransSetDate (Transaction *trans, int day, int mon, int year)
|
||||
{
|
||||
Split *split;
|
||||
Account *acc;
|
||||
int i=0;
|
||||
|
||||
trans->date.year = year;
|
||||
trans->date.month = mon;
|
||||
@ -672,24 +650,18 @@ xaccTransSetDate (Transaction *trans, int day, int mon, int year)
|
||||
* order.
|
||||
*/
|
||||
|
||||
split = &(trans->source_split);
|
||||
acc = (Account *) split->acc;
|
||||
xaccAccountRemoveSplit (acc, split);
|
||||
xaccAccountInsertSplit (acc, split);
|
||||
xaccRecomputeBalance (acc);
|
||||
assert (trans->splits);
|
||||
|
||||
if (trans->dest_splits) {
|
||||
int i=0;
|
||||
split = trans->dest_splits[i];
|
||||
while (split) {
|
||||
acc = (Account *) split->acc;
|
||||
xaccAccountRemoveSplit (acc, split);
|
||||
xaccAccountInsertSplit (acc, split);
|
||||
xaccRecomputeBalance (acc);
|
||||
i=0;
|
||||
split = trans->splits[i];
|
||||
while (split) {
|
||||
acc = (Account *) split->acc;
|
||||
xaccAccountRemoveSplit (acc, split);
|
||||
xaccAccountInsertSplit (acc, split);
|
||||
xaccRecomputeBalance (acc);
|
||||
|
||||
i++;
|
||||
split = trans->dest_splits[i];
|
||||
}
|
||||
i++;
|
||||
split = trans->splits[i];
|
||||
}
|
||||
}
|
||||
|
||||
@ -733,67 +705,55 @@ xaccTransSetDescription (Transaction *trans, const char *desc)
|
||||
MarkChanged (trans);
|
||||
}
|
||||
|
||||
void
|
||||
xaccTransSetMemo (Transaction *trans, const char *memo)
|
||||
{
|
||||
char * tmp = strdup (memo);
|
||||
if (trans->source_split.memo) free (trans->source_split.memo);
|
||||
trans->source_split.memo = tmp;
|
||||
MARK_SPLIT (&(trans->source_split));
|
||||
#define SET_TRANS_FIELD(trans,field,value) \
|
||||
{ \
|
||||
if (!trans) return; \
|
||||
\
|
||||
/* the engine *must* always be internally consistent */ \
|
||||
assert (trans->splits); \
|
||||
\
|
||||
if (force_double_entry) { \
|
||||
assert (trans->splits[0]); \
|
||||
assert (trans->splits[1]); \
|
||||
} \
|
||||
\
|
||||
if (0x0 != trans->splits[0]) { \
|
||||
char * tmp = strdup (value); \
|
||||
free (trans->splits[0]->field); \
|
||||
trans->splits[0]->field = tmp; \
|
||||
MARK_SPLIT (trans->splits[0]); \
|
||||
\
|
||||
/* if there are just two splits, then keep them in sync. */\
|
||||
if (0x0 != trans->splits[1]) { \
|
||||
if (0x0 == trans->splits[2]) { \
|
||||
free (trans->splits[1]->field); \
|
||||
trans->splits[1]->field = strdup (tmp); \
|
||||
MARK_SPLIT (trans->splits[1]); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
/* if there is only one split, then keep memos in sync. */
|
||||
if (trans->dest_splits) {
|
||||
if (0x0 != trans->dest_splits[0]) {
|
||||
if (0x0 == trans->dest_splits[1]) {
|
||||
free (trans->dest_splits[0]->memo);
|
||||
trans->dest_splits[0]->memo = strdup (tmp);
|
||||
MARK_SPLIT (trans->dest_splits[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
void
|
||||
xaccTransSetMemo (Transaction *trans, const char *mimeo)
|
||||
{
|
||||
SET_TRANS_FIELD (trans, memo, mimeo);
|
||||
}
|
||||
|
||||
void
|
||||
xaccTransSetAction (Transaction *trans, const char *actn)
|
||||
{
|
||||
char * tmp = strdup (actn);
|
||||
|
||||
if (trans->source_split.action) free (trans->source_split.action);
|
||||
trans->source_split.action = tmp;
|
||||
MARK_SPLIT (&(trans->source_split));
|
||||
|
||||
/* if there is only one split, then keep action in sync. */
|
||||
if (trans->dest_splits) {
|
||||
if (0x0 != trans->dest_splits[0]) {
|
||||
if (0x0 == trans->dest_splits[1]) {
|
||||
free (trans->dest_splits[0]->action);
|
||||
trans->dest_splits[0]->action = strdup (tmp);
|
||||
MARK_SPLIT (trans->dest_splits[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
xaccTransSetReconcile (Transaction *trans, char recn)
|
||||
{
|
||||
trans->source_split.reconciled = recn;
|
||||
MARK_SPLIT (&(trans->source_split));
|
||||
SET_TRANS_FIELD (trans, action, actn);
|
||||
}
|
||||
|
||||
/********************************************************************\
|
||||
\********************************************************************/
|
||||
Split *
|
||||
xaccTransGetSourceSplit (Transaction *trans)
|
||||
{
|
||||
return (&(trans->source_split));
|
||||
}
|
||||
|
||||
Split *
|
||||
xaccTransGetDestSplit (Transaction *trans, int i)
|
||||
xaccTransGetSplit (Transaction *trans, int i)
|
||||
{
|
||||
if (trans->dest_splits) {
|
||||
return (trans->dest_splits[i]);
|
||||
if (trans->splits) {
|
||||
return (trans->splits[i]);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@ -827,13 +787,7 @@ xaccTransGetDateStr (Transaction *trans)
|
||||
int
|
||||
xaccTransCountSplits (Transaction *trans)
|
||||
{
|
||||
return (xaccCountSplits (trans->dest_splits));
|
||||
}
|
||||
|
||||
int
|
||||
xaccTransIsSource (Transaction *trans, Split *split)
|
||||
{
|
||||
return (split == &(trans->source_split));
|
||||
return (xaccCountSplits (trans->splits));
|
||||
}
|
||||
|
||||
/********************************************************************\
|
||||
|
@ -64,10 +64,6 @@ typedef struct _transaction Transaction;
|
||||
Transaction * xaccMallocTransaction (void); /* mallocs and inits */
|
||||
void xaccInitTransaction (Transaction *);/* clears a trans struct */
|
||||
|
||||
/* freeTransaction only does so if the transaction is not part of an
|
||||
* account. (i.e. if none of the member splits are in an account). */
|
||||
void xaccFreeTransaction (Transaction *);
|
||||
|
||||
void xaccTransBeginEdit (Transaction *);
|
||||
void xaccTransCommitEdit (Transaction *);
|
||||
|
||||
@ -81,64 +77,39 @@ void xaccTransSetNum (Transaction *, const char *);
|
||||
void xaccTransSetDescription (Transaction *, const char *);
|
||||
void xaccTransSetMemo (Transaction *, const char *);
|
||||
void xaccTransSetAction (Transaction *, const char *);
|
||||
void xaccTransSetReconcile (Transaction *, char);
|
||||
|
||||
void xaccTransAppendSplit (Transaction *, Split *);
|
||||
void xaccTransRemoveSplit (Transaction *, Split *);
|
||||
|
||||
/*
|
||||
* HACK ALERT *** this algorithm is wrong. Needs fixing.
|
||||
* 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.
|
||||
* The xaccSplitDestroy() method will update its parent account and
|
||||
* transaction in a consistent maner, resulting in the complete
|
||||
* unlinking of the split, and the freeing of it's associated memory.
|
||||
* The goal of this routine is to perform the removal and destruction
|
||||
* of the split in an atomic fashion, with no chance of accidentally
|
||||
* leaving the accounting structure out-of-balance or otherwise
|
||||
* inconsistent.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* The xaccTransRebalance() routine merely calls xaccSplitRebalance()
|
||||
* on the source split.
|
||||
* If the parent transaction of the split has three or more splits
|
||||
* in it, then only this one split is unlinked. If the parent
|
||||
* transaction has only two splits in it (and thus, this is one of
|
||||
* them), then both splits and the transaction are destroyed.
|
||||
*/
|
||||
|
||||
void xaccSplitRebalance (Split *);
|
||||
void xaccTransRebalance (Transaction *);
|
||||
void xaccSplitDestroy (Split *);
|
||||
|
||||
/* ------------- gets --------------- */
|
||||
/* return pointer to the source split */
|
||||
Split * xaccTransGetSourceSplit (Transaction *);
|
||||
Split * xaccTransGetDestSplit (Transaction *trans, int i);
|
||||
/* return pointer to each of the splits */
|
||||
Split * xaccTransGetSplit (Transaction *trans, int i);
|
||||
|
||||
char * xaccTransGetNum (Transaction *);
|
||||
char * xaccTransGetDescription (Transaction *trans);
|
||||
char * xaccTransGetDescription (Transaction *);
|
||||
Date * xaccTransGetDate (Transaction *);
|
||||
char * xaccTransGetDateStr (Transaction *);
|
||||
|
||||
/* return the number of destination splits */
|
||||
/* return the number of splits */
|
||||
int xaccTransCountSplits (Transaction *trans);
|
||||
|
||||
/* returns non-zero value if split is source split */
|
||||
int xaccTransIsSource (Transaction *, Split *);
|
||||
|
||||
|
||||
Split * xaccMallocSplit (void);
|
||||
void xaccInitSplit (Split *); /* clears a split struct */
|
||||
void xaccFreeSplit (Split *); /* frees memory */
|
||||
int xaccCountSplits (Split **sarray);
|
||||
|
||||
void xaccSplitSetMemo (Split *, const char *);
|
||||
|
@ -8,6 +8,13 @@
|
||||
* outside of the engine should *never* access any of the structure
|
||||
* members directly.
|
||||
*
|
||||
* Note that this header file also defines prototypes for various
|
||||
* routines that perform sub-atomic updates of the accounting
|
||||
* structures. If these routines are not used properly, they
|
||||
* can result in inconsistent, unbalanced accounting structures.
|
||||
* In other words, thier use is dangerous, and thier use outside
|
||||
* of the scope of the engine is forbidden.
|
||||
*
|
||||
*/
|
||||
|
||||
/********************************************************************\
|
||||
@ -44,14 +51,17 @@
|
||||
|
||||
|
||||
/** STRUCTS *********************************************************/
|
||||
/* The debit & credit pointers are used to implement a double-entry
|
||||
* accounting system. Basically, the idea with double entry is that
|
||||
* there is always an account that is debited, and another that is
|
||||
* credited. These two pointers identify the two accounts.
|
||||
/*
|
||||
* Double-entry is forced by having at least two splits in every
|
||||
* transaction. By convention, (and only by convention, not by
|
||||
* any inate requirement), the first split is considered to be
|
||||
* the source split or the crediting split, and the others are
|
||||
* the destination, or debiting splits. The grand total of all
|
||||
* of the splits must always be kept zero.
|
||||
*/
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
@ -85,8 +95,7 @@ struct _transaction
|
||||
Date date; /* transaction date */
|
||||
char * description;
|
||||
|
||||
Split source_split; /* source (creidted) account */
|
||||
Split **dest_splits; /* list of splits, null terminated */
|
||||
Split **splits; /* list of splits, null terminated */
|
||||
|
||||
char write_flag; /* used only during file IO */
|
||||
|
||||
@ -96,4 +105,41 @@ struct _transaction
|
||||
};
|
||||
|
||||
|
||||
/* freeTransaction only does so if the transaction is not part of an
|
||||
* account. (i.e. if none of the member splits are in an account). */
|
||||
void xaccFreeTransaction (Transaction *);
|
||||
|
||||
void xaccFreeSplit (Split *); /* frees memory */
|
||||
|
||||
|
||||
/*
|
||||
* 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 (i.e. is not
|
||||
* the first split), then the total value of the destination
|
||||
* splits is computed, and the value of the source split (ie.
|
||||
* the first 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 source 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 *);
|
||||
|
||||
|
||||
#endif /* __XACC_TRANSACTION_P_H__ */
|
||||
|
Loading…
Reference in New Issue
Block a user