merg changes from the cap-gains7 branch. These include:

-- numerous fixes to make cap-gains work
-- crude ideas about a generic constraints system to keep data in engine
   correct.
After these changes, all known cap-gains bugs are fixed, and most cap-gains
features are done.  Have at it.


git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@9378 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
Linas Vepstas 2003-09-20 22:53:28 +00:00
parent a3792d997c
commit ce93bd96a2
9 changed files with 184 additions and 109 deletions

View File

@ -1067,7 +1067,9 @@ xaccAccountInsertSplit (Account *acc, Split *split)
/* Setting the amount casues a conversion to the new account's
* denominator AKA 'SCU Smallest Currency Unit'. */
xaccSplitSetAmount(split, old_amt);
/* xaccSplitSetAmount(split, old_amt); */
split->amount = gnc_numeric_convert (old_amt,
xaccAccountGetCommoditySCU(acc), GNC_RND_ROUND);
xaccTransCommitEdit(trans);
xaccAccountCommitEdit(acc);
LEAVE ("(acc=%p, split=%p)", acc, split);
@ -1169,17 +1171,23 @@ xaccAccountRecomputeBalance (Account * acc)
cleared_balance = acc->starting_cleared_balance;
reconciled_balance = acc->starting_reconciled_balance;
for(lp = acc->splits; lp; lp = lp->next) {
PINFO ("acct=%s starting baln=%lld/%lld", acc->accountName,
balance.num, balance.denom);
for(lp = acc->splits; lp; lp = lp->next)
{
Split *split = (Split *) lp->data;
gnc_numeric amt = xaccSplitGetAmount (split);
balance = gnc_numeric_add_fixed(balance, amt);
if (NREC != split->reconciled)
{
cleared_balance = gnc_numeric_add_fixed(cleared_balance, amt);
}
if (YREC == split->reconciled ||
FREC == split->reconciled) {
FREC == split->reconciled)
{
reconciled_balance =
gnc_numeric_add_fixed(reconciled_balance, amt);
}
@ -1879,6 +1887,9 @@ xaccAccountGetPresentBalance (Account *account)
/********************************************************************\
\********************************************************************/
/* XXX TODO: These 'GetBal' routines should be moved to some
* utility area outside of the core account engine area.
*/
/*
* Convert a balance from one currency to another.
@ -1916,12 +1927,12 @@ xaccAccountGetXxxBalanceInCurrency (Account *account,
{
gnc_numeric balance;
if (!account || !fn || !report_currency)
return gnc_numeric_zero ();
if (!account || !fn || !report_currency) return gnc_numeric_zero ();
balance = fn(account);
return xaccAccountConvertBalanceToCurrency(account, balance,
balance = xaccAccountConvertBalanceToCurrency(account, balance,
account->commodity,
report_currency);
return balance;
}
/*
@ -1993,9 +2004,12 @@ xaccAccountGetBalanceInCurrency (Account *account,
gnc_commodity *report_commodity,
gboolean include_children)
{
return
xaccAccountGetXxxBalanceInCurrencyRecursive (account, xaccAccountGetBalance,
gnc_numeric rc;
rc = xaccAccountGetXxxBalanceInCurrencyRecursive (account,
xaccAccountGetBalance,
report_commodity, include_children);
PINFO (" baln=%lld/%lld", rc.num, rc.denom);
return rc;
}

View File

@ -98,6 +98,7 @@ xaccLotFill (GNCLot *lot)
{
gnc_numeric lot_baln;
Account *acc;
Split *split;
if (!lot) return;
acc = lot->account;
@ -108,15 +109,17 @@ xaccLotFill (GNCLot *lot)
lot_baln = gnc_lot_get_balance (lot);
if (gnc_numeric_zero_p (lot_baln)) return;
split = FIFOPolicyGetSplit (lot, NULL);
if (!split) return; /* Handle the common case */
xaccAccountBeginEdit (acc);
/* Loop until we've filled up the lot, (i.e. till the
* balance goes to zero) or there are no splits left. */
while (1)
{
Split *split, *subsplit;
Split *subsplit;
split = FIFOPolicyGetSplit (lot, NULL);
subsplit = xaccSplitAssignToLot (split, lot);
if (subsplit == split)
{
@ -127,6 +130,9 @@ xaccLotFill (GNCLot *lot)
lot_baln = gnc_lot_get_balance (lot);
if (gnc_numeric_zero_p (lot_baln)) break;
split = FIFOPolicyGetSplit (lot, NULL);
if (!split) break;
}
xaccAccountCommitEdit (acc);
LEAVE ("acc=%s", acc->accountName);
@ -193,9 +199,10 @@ xaccLotScrubDoubleBalance (GNCLot *lot)
}
/* Now, total up the values */
value = gnc_numeric_add_fixed (value, xaccSplitGetValue (s));
PINFO ("Split value=%s Accum Lot value=%s",
gnc_numeric_to_string (xaccSplitGetValue(s)),
value = gnc_numeric_add (value, xaccSplitGetValue (s),
GNC_DENOM_AUTO, GNC_DENOM_EXACT);
PINFO ("Split=%p value=%s Accum Lot value=%s", s,
gnc_numeric_to_string (s->value),
gnc_numeric_to_string (value));
}

View File

@ -220,6 +220,7 @@ restart:
{
Split *s = node->data;
if (xaccSplitGetLot (s) != lot) continue;
if (s == split) continue;
/* OK, this split is in the same lot (and thus same account)
* as the indicated split. It must be a subsplit (although
@ -279,14 +280,15 @@ restart:
/* ================================================================= */
void
gboolean
xaccScrubLot (GNCLot *lot)
{
gboolean splits_deleted = FALSE;
gnc_numeric lot_baln;
gboolean opening_baln_is_pos, lot_baln_is_pos;
Account *acc;
if (!lot) return;
if (!lot) return FALSE;
ENTER (" ");
acc = gnc_lot_get_account (lot);
@ -324,7 +326,7 @@ rethin:
xaccLotFill (lot);
/* Make sure there are no subsplits. */
xaccScrubMergeLotSubSplits (lot);
splits_deleted = xaccScrubMergeLotSubSplits (lot);
}
/* Now re-compute cap gains, and then double-check that. */
@ -332,7 +334,8 @@ rethin:
xaccLotScrubDoubleBalance (lot);
xaccAccountCommitEdit(acc);
LEAVE (" ");
LEAVE (" deleted=%d", splits_deleted);
return splits_deleted;
}
/* ========================== END OF FILE ========================= */

View File

@ -78,8 +78,12 @@ gboolean xaccScrubMergeLotSubSplits (GNCLot *lot);
* split values is gaurenteed to throw off lot balances.
* This routine may end up closing the lot, or at least trying
* to. It will also cause cap gains to be recomputed.
*
* Scrubbing the lot may cause subsplits to be merged together,
* i.e. for splits to be deleted. This routine returns true if
* any splits were deleted.
*/
void xaccScrubLot (GNCLot *lot);
gboolean xaccScrubLot (GNCLot *lot);
#endif /* XACC_SCRUB3_H */
/** @} */

View File

@ -33,6 +33,7 @@
#include "AccountP.h"
#include "Group.h"
#include "Scrub3.h"
#include "TransactionP.h"
#include "TransLog.h"
#include "cap-gains.h"
@ -87,15 +88,13 @@ const char *void_former_notes_str = "void-former-notes";
/* This static indicates the debugging module that this .o belongs to. */
static short module = MOD_ENGINE;
G_INLINE_FUNC void check_open (Transaction *trans);
G_INLINE_FUNC void
check_open (Transaction *trans)
{
if (trans && 0 >= trans->editlevel)
{
PERR ("transaction %p not open for editing\n", trans);
PERR ("\t%s:%d \n", __FILE__, __LINE__);
PERR ("transaction %p not open for editing", trans);
}
}
@ -782,10 +781,10 @@ void
xaccSplitSetAmount (Split *s, gnc_numeric amt)
{
if(!s) return;
ENTER ("old amt=%lld/%lld new amt=%lld/%lld",
ENTER ("split=%p old amt=%lld/%lld new amt=%lld/%lld", s,
s->amount.num, s->amount.denom, amt.num, amt.denom);
check_open (s->parent);
check_open (s->parent);
s->amount = gnc_numeric_convert(amt, get_commodity_denom(s), GNC_RND_ROUND);
SET_GAINS_ADIRTY(s);
@ -798,10 +797,10 @@ void
xaccSplitSetValue (Split *s, gnc_numeric amt)
{
if(!s) return;
ENTER ("old val=%lld/%lld new val=%lld/%lld",
ENTER ("split=%p old val=%lld/%lld new val=%lld/%lld", s,
s->value.num, s->value.denom, amt.num, amt.denom);
check_open (s->parent);
check_open (s->parent);
s->value = gnc_numeric_convert(amt, get_currency_denom(s), GNC_RND_ROUND);
SET_GAINS_VDIRTY(s);
@ -1455,7 +1454,7 @@ xaccSplitsComputeValue (GList *splits, Split * skip_me,
/* The split-editor often sends us 'temp' splits whose account
* hasn't yet been set. Be lenient, and assume an implied base
* currency. If theres a problem later, teh scrub routines will
* currency. If theres a problem later, the scrub routines will
* pick it up.
*/
if (NULL == s->acc)
@ -1674,6 +1673,67 @@ do_destroy (Transaction *trans)
/********************************************************************\
\********************************************************************/
/** The xaccScrubGainsDate() routine is used to keep the posted date
* of gains splis in sync with the posted date of the transaction
* that caused the gains.
*
* The posted date is kept in sync using a lazy-evaluation scheme.
* If xaccTransactionSetDatePosted() is called, the date change is
* accepted, and the split is marked date-dirty. If the posted date
* is queried for (using GetDatePosted()), then the transaction is
* evaluated. If its a gains-transaction, then it's date is copied
* from the source transaction that created the gains.
*/
static inline void
xaccScrubGainsDate (Transaction *trans)
{
SplitList *node;
Timespec ts = {0,0};
gboolean do_set;
restart_search:
do_set = FALSE;
for (node = trans->splits; node; node=node->next)
{
Split *s = node->data;
if (GAINS_STATUS_UNKNOWN == s->gains) xaccSplitDetermineGainStatus(s);
if ((GAINS_STATUS_GAINS & s->gains) &&
s->gains_split &&
((s->gains_split->gains & GAINS_STATUS_DATE_DIRTY) ||
(s->gains & GAINS_STATUS_DATE_DIRTY)))
{
Transaction *source_trans = s->gains_split->parent;
ts = source_trans->date_posted;
do_set = TRUE;
s->gains &= ~GAINS_STATUS_DATE_DIRTY;
s->gains_split->gains &= ~GAINS_STATUS_DATE_DIRTY;
break;
}
}
if (do_set)
{
xaccTransBeginEdit (trans);
xaccTransSetDatePostedTS(trans, &ts);
xaccTransCommitEdit (trans);
for (node = trans->splits; node; node=node->next)
{
Split *s = node->data;
s->gains &= ~GAINS_STATUS_DATE_DIRTY;
}
goto restart_search;
}
}
/********************************************************************\
\********************************************************************/
/* Temporary hack for data consitency */
static int scrub_data = 1;
void xaccEnableDataScrubbing(void) { scrub_data = 1; }
void xaccDisableDataScrubbing(void) { scrub_data = 0; }
void
xaccTransCommitEdit (Transaction *trans)
@ -1697,6 +1757,55 @@ xaccTransCommitEdit (Transaction *trans)
* call to xaccTransCommitEdit. */
trans->editlevel++;
/* Before commiting the transaction, we're gonna enforce certain
* constraints. In particular, we want to enforce the cap-gains
* and the balanced lot constraints. These constraints might
* change the numbr of splits in this transaction, and the
* transaction itself might be deleted. This is also why
* we can't really enforce these constraints elsewhere: they
* can cause pointers to splits and transactions to disapear out
* from under the holder.
*/
if (scrub_data)
{
/* Lock down posted date to be synced to the source of cap gains */
xaccScrubGainsDate(trans);
/* Fix up split value */
if (trans->splits && !(trans->do_free))
{
SplitList *node;
/* Fix up split amount */
restart:
for (node=trans->splits; node; node=node->next)
{
split = node->data;
CHECK_GAINS_STATUS (split);
if (split->gains & GAINS_STATUS_ADIRTY)
{
gboolean altered = FALSE;
split->gains |= ~GAINS_STATUS_ADIRTY;
if (split->lot) altered = xaccScrubLot (split->lot);
if (altered) goto restart;
}
}
/* Fix up gains split value */
for (node=trans->splits; node; node=node->next)
{
split = node->data;
if ((split->gains & GAINS_STATUS_VDIRTY) ||
(split->gains_split &&
(split->gains_split->gains & GAINS_STATUS_VDIRTY)))
{
xaccSplitComputeCapGains (split, NULL);
}
}
}
}
/* At this point, we check to see if we have a valid transaction.
* There are two possiblities:
* 1) Its more or less OK, and needs a little cleanup
@ -2696,64 +2805,11 @@ xaccTransGetNotes (const Transaction *trans)
/********************************************************************\
\********************************************************************/
/** The xaccScrubGainsDate() routine is used to keep the posted date
* of gains splis in sync with the posted date of the transaction
* that caused the gains.
*
* The posted date is kept in sync using a lazy-evaluation scheme.
* If xaccTransactionSetDatePosted() is called, the date change is
* accepted, and the split is marked date-dirty. If the posted date
* is queried for (using GetDatePosted()), then the transaction is
* evaluated. If its a gains-transaction, then it's date is copied
* from the source transaction that created the gains.
*/
static inline void
xaccScrubGainsDate (Transaction *trans)
{
SplitList *node;
Timespec ts = {0,0};
gboolean do_set;
restart_search:
do_set = FALSE;
for (node = trans->splits; node; node=node->next)
{
Split *s = node->data;
if (GAINS_STATUS_UNKNOWN == s->gains) xaccSplitDetermineGainStatus(s);
if ((GAINS_STATUS_GAINS & s->gains) &&
s->gains_split &&
((s->gains_split->gains & GAINS_STATUS_DATE_DIRTY) ||
(s->gains & GAINS_STATUS_DATE_DIRTY)))
{
Transaction *source_trans = s->gains_split->parent;
ts = source_trans->date_posted;
do_set = TRUE;
s->gains &= ~GAINS_STATUS_DATE_DIRTY;
s->gains_split->gains &= ~GAINS_STATUS_DATE_DIRTY;
break;
}
}
if (do_set)
{
xaccTransBeginEdit (trans);
xaccTransSetDatePostedTS(trans, &ts);
xaccTransCommitEdit (trans);
for (node = trans->splits; node; node=node->next)
{
Split *s = node->data;
s->gains &= ~GAINS_STATUS_DATE_DIRTY;
}
goto restart_search;
}
}
time_t
xaccTransGetDate (const Transaction *trans)
{
if (!trans) return 0;
xaccScrubGainsDate((Transaction *) trans); /* XXX wrong not const ! */
return (trans->date_posted.tv_sec);
}
@ -2761,7 +2817,6 @@ void
xaccTransGetDatePostedTS (const Transaction *trans, Timespec *ts)
{
if (!trans || !ts) return;
xaccScrubGainsDate((Transaction *) trans); /* XXX wrong not const ! */
*ts = (trans->date_posted);
}
@ -2777,7 +2832,6 @@ xaccTransRetDatePostedTS (const Transaction *trans)
{
Timespec ts = {0, 0};
if (!trans) return ts;
xaccScrubGainsDate((Transaction *) trans); /* XXX wrong not const ! */
return (trans->date_posted);
}
@ -3040,13 +3094,6 @@ xaccSplitGetAmount (const Split * cs)
{
Split *split = (Split *) cs;
if (!split) return gnc_numeric_zero();
/* The value of cap-gains splits is slave to the
* transaction that's actually causing the gains.
XXX implementation not finished!!
*/
CHECK_GAINS_STATUS(split);
return split->amount;
}
@ -3055,21 +3102,6 @@ xaccSplitGetValue (const Split * cs)
{
Split *split = (Split *) cs;
if (!split) return gnc_numeric_zero();
/* The value of cap-gains splits is slave to the
* transaction that's actually causing the gains.
XXX this test is wrong, it also needs to check for changed amount
which will should casue lot to recomputed!
*/
CHECK_GAINS_STATUS(split);
if ((split->gains & GAINS_STATUS_GAINS) &&
split->gains_split &&
(split->gains_split->gains & GAINS_STATUS_VDIRTY))
{
xaccSplitComputeCapGains (split, NULL);
split->gains_split->gains |= ~GAINS_STATUS_VDIRTY;
}
return split->value;
}

View File

@ -147,7 +147,6 @@ Transaction * xaccTransLookup (const GUID *guid, QofBook *book);
if there is no such transaction. */
Transaction * xaccTransLookupDirect (GUID guid, QofBook *book);
/** \warning XXX FIXME
* gnc_book_count_transactions is a utility function,
* probably needs to be moved to a utility file somewhere.

View File

@ -267,13 +267,26 @@ gint32 xaccTransGetVersion (const Transaction*);
gboolean xaccSplitRegister (void);
gboolean xaccTransRegister (void);
/*
* The xaccTransactionGetBackend() subroutine will find the
/* The xaccTransactionGetBackend() subroutine will find the
* persistent-data storage backend associated with this
* transaction.
*/
QofBackend * xaccTransactionGetBackend (Transaction *trans);
/* The xaccEnable/DisableDataScrubbing() routines affect what
* happens during transaction commit. When scrubbing is enabled,
* then transactions are fixed up during transaction commit,
* so that only consistent transactions are commited to the engine.
* However, when data is being loaded from a backend (in particular,
* from the file backend), the data might not be consistent until
* its completely loaded. In particular, gains transactions might
* be loaded at a different time than the transactions that casued
* the gains. Thus, scrubbing needs do be disabled during file
* load. These routines enable that.
*/
void xaccEnableDataScrubbing(void);
void xaccDisableDataScrubbing(void);
/* The xaccSplitDetermineGainStatus() routine will analyze the
* the split, and try to set the internal status flags
* appropriately for the split. These flags indicate if the split

View File

@ -545,6 +545,7 @@ xaccSplitGetCapGainsSplit (Split *split)
gains_split = qof_entity_lookup (qof_book_get_entity_table(split->book),
gains_guid, GNC_ID_SPLIT);
PINFO ("split=%p has gains-split=%p", split, gains_split);
return gains_split;
}
@ -565,7 +566,8 @@ xaccSplitComputeCapGains(Split *split, Account *gain_acc)
if (!lot) return;
currency = split->parent->common_currency;
ENTER ("split=%p lot=%s", split,
ENTER ("split=%p gains=%p status=0x%x lot=%s", split,
split->gains_split, split->gains,
kvp_frame_get_string (gnc_lot_get_slots (lot), "/title"));
/* Make sure the status flags and pointers are initialized */
@ -696,7 +698,8 @@ XXX this should be a part of a scrub routine !
* If there is, adjust its value as appropriate. Else, create
* a new gains transaction.
*/
lot_split = xaccSplitGetCapGainsSplit (split);
/* lot_split = xaccSplitGetCapGainsSplit (split); */
lot_split = split->gains_split;
if (NULL == lot_split)
{

View File

@ -209,7 +209,7 @@ gnc_lot_get_balance (GNCLot *lot)
{
Split *s = node->data;
gnc_numeric amt = xaccSplitGetAmount (s);
baln = gnc_numeric_add (baln, amt, GNC_DENOM_AUTO, GNC_DENOM_FIXED);
baln = gnc_numeric_add_fixed (baln, amt);
}
/* cache a zero balance as a closed lot */