Cap Gains
[GnuCash Engine: Core, Non-GUI Accounting Functions]


Files

file  cap-gains.h
 Utilities to Automatically Compute Capital Gains/Losses.

Functions

gnc_numeric xaccSplitGetCapGains (Split *)
gboolean xaccAccountHasTrades (const Account *)
GNCLotxaccAccountFindEarliestOpenLot (Account *acc, gnc_numeric sign, gnc_commodity *currency)
GNCLotxaccAccountFindLatestOpenLot (Account *acc, gnc_numeric sign, gnc_commodity *currency)
AccountxaccAccountGetDefaultGainAccount (const Account *acc, const gnc_commodity *currency)
void xaccAccountSetDefaultGainAccount (Account *acc, const Account *gains_acct)
SplitxaccSplitGetCapGainsSplit (const Split *)
SplitxaccSplitGetGainsSourceSplit (const Split *)
gboolean xaccSplitAssign (Split *split)
SplitxaccSplitAssignToLot (Split *split, GNCLot *lot)
void xaccSplitComputeCapGains (Split *split, Account *gain_acc)
void xaccLotComputeCapGains (GNCLot *lot, Account *gain_acc)

Detailed Description

This file implements the various routines to automatically compute and handle Cap Gains/Losses resulting from trading activities. Some of these routines might have broader applicability, for handling depreciation & etc.

This code is under development, and is 'beta': we think we're mostly done, and we've tested and "things work for us", but there may still be something missing, and there might still be some bugs.

This code does not currently handle tax distinctions, e.g the different tax treatment that short-term and long-term cap gains have.

The computation of (Realized) Gains/Losses is performed automatically by the lot "scrub" routines, using a "double-balance" algorithm. Every split has two numbers associated with it: an "amount", which is the number of items that a split describes, and the "value", which is the cost of those items. In a closed lot, the grand-total amount of items in the lot is zero: the number of items bought equals the number of items sold; thus the amount-balance is zero. But since the purchase/sale of the items in the lot typically happen at different prices, there will typically be a gain/loss. This gain/loss is the grand-total value of all the items in the lot (total costs minus total income).

In order to properly account for the gains/losses, an "adjusting split" is added that brings the total gains/losses back to exactly zero (this is the second "balance" of "double balance"). This adjusting split will have an amount of zero (no items are involved) but have a non-zero value (equal to the total gain/loss). This split can then participate in a "gains transaction" which records the gains in another account. Thus, for example, if you record $300 in your bank account due to the purchase and then the sale of some item, the "gains transaction" will record $300 in income in an income account. Thus, the change in the bank balance is always reflected by an equal change in income, assuring that the books are balanced.

Notes about auto-recompute: If the amount in a split is changed, then the lot has to be recomputed. This has a potential trickle-through effect on all later lots. Ideally, later lots are dissolved, and recomputed. However, some lots may have been user-hand-built. These should be left alone.

ToDo: o XXX Need to create a data-integrity scrubber, tht makes sure that the various flags, and pointers & etc. match.


Function Documentation

GNCLot* xaccAccountFindEarliestOpenLot ( Account acc,
gnc_numeric  sign,
gnc_commodity currency 
)

The xaccAccountFindEarliestOpenLot() method is a handy utility routine for finding the earliest open lot in an account whose lot balance is *opposite* to the passed argument 'sign'. By 'earliest lot', we mean the lot that has a split with the earliest 'date_posted'. The sign comparison helps identify a lot that can be added to: usually, one wants to add splits to a lot so that the balance only decreases. If 'currency' is non-null, then this attempts to find a lot whose opening transaction has the same currency.

Definition at line 189 of file cap-gains.c.

00191 {
00192     GNCLot *lot;
00193     ENTER (" sign=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT, sign.num,
00194            sign.denom);
00195 
00196     lot = xaccAccountFindOpenLot (acc, sign, currency,
00197                                   G_MAXINT64, earliest_pred);
00198     LEAVE ("found lot=%p %s baln=%s", lot, gnc_lot_get_title (lot),
00199            gnc_num_dbg_to_string(gnc_lot_get_balance(lot)));
00200     return lot;
00201 }

Account* xaccAccountGetDefaultGainAccount ( const Account acc,
const gnc_commodity currency 
)

The xaccAccountGetDefaultGainAccount() routine will return the account to which realized gains/losses may be posted. Because gains may be in different currencies, one must specify the currency type in which the gains will be posted. This routine does nothing more than return the value of the "/lot-mgmt/gains-act/XXX" key, where XXX is the unique currency name. IOf there is no default account for this currency, NULL will be returned.

Definition at line 294 of file cap-gains.c.

00295 {
00296     Account *gain_acct = NULL;
00297     KvpFrame *cwd;
00298     KvpValue *vvv;
00299     GUID * gain_acct_guid;
00300     const char * cur_name;
00301 
00302     if (!acc || !currency) return NULL;
00303 
00304     cwd = xaccAccountGetSlots (acc);
00305     cwd = kvp_frame_get_frame_slash (cwd, "/lot-mgmt/gains-act/");
00306 
00307     /* Accounts are indexed by thier unique currency name */
00308     cur_name = gnc_commodity_get_unique_name (currency);
00309     vvv = kvp_frame_get_slot (cwd, cur_name);
00310     gain_acct_guid = kvp_value_get_guid (vvv);
00311 
00312     gain_acct = xaccAccountLookup (gain_acct_guid, qof_instance_get_book(acc));
00313     return gain_acct;
00314 }

gboolean xaccAccountHasTrades ( const Account  ) 

The xaccAccountHasTrades() method checks to see if the indicated account is used in the trading of commodities. A 'trading' account will contain transactions whose transaction currency is not the same as the account commodity. The existance of such transactions is the very definition of a 'trade'. This routine returns TRUE if this is a trading account, else it returns FALSE.

Definition at line 78 of file cap-gains.c.

00079 {
00080     gnc_commodity *acc_comm;
00081     SplitList *splits, *node;
00082 
00083     if (!acc) return FALSE;
00084 
00085     if (xaccAccountIsPriced (acc))
00086         return TRUE;
00087 
00088     acc_comm = xaccAccountGetCommodity(acc);
00089 
00090     splits = xaccAccountGetSplitList(acc);
00091     for (node = splits; node; node = node->next)
00092     {
00093         Split *s = node->data;
00094         Transaction *t = s->parent;
00095         if (acc_comm != t->common_currency) return TRUE;
00096     }
00097 
00098     return FALSE;
00099 }

void xaccAccountSetDefaultGainAccount ( Account acc,
const Account gains_acct 
)

The xaccAccountSetDefaultGainAccount() routine can be used to set the account to which realized gains/losses will be posted by default. This routine does nothing more than set value of the "/lot-mgmt/gains-act/XXX" key, where XXX is the unique currency name of the currency of gains account.

Definition at line 268 of file cap-gains.c.

00269 {
00270     KvpFrame *cwd;
00271     KvpValue *vvv;
00272     const char * cur_name;
00273     gnc_commodity *acc_comm;
00274 
00275     if (!acc || !gain_acct) return;
00276 
00277     cwd = xaccAccountGetSlots (acc);
00278     cwd = kvp_frame_get_frame_slash (cwd, "/lot-mgmt/gains-act/");
00279 
00280     /* Accounts are indexed by thier unique currency name */
00281     acc_comm = xaccAccountGetCommodity(acc);
00282     cur_name = gnc_commodity_get_unique_name (acc_comm);
00283 
00284     xaccAccountBeginEdit (acc);
00285     vvv = kvp_value_new_guid (xaccAccountGetGUID (gain_acct));
00286     kvp_frame_set_slot_nc (cwd, cur_name, vvv);
00287     qof_instance_set_slots(QOF_INSTANCE(acc), acc->inst.kvp_data);
00288     xaccAccountCommitEdit (acc);
00289 }

gboolean xaccSplitAssign ( Split split  ) 

The`xaccSplitAssign() routine will take the indicated split and, if it doesn't already belong to a lot, it will attempt to assign it to an appropriate lot. If the split already belongs to a Lot, this routine does nothing. If there are no open Lots, this routine will create a new lot and place the split into it. If there's an open lot, and its big enough to accept the split in it's entirety, then the split will be placed into that lot. If the split is too big to fit into the currently open lot, it will be busted up into two (or more) pieces, and each placed into a lot accordingly. If the split needed to be broken up into several pieces, this routine will return TRUE, else it returns FALSE.

If the split had to be broken up, kvp markup in the "/lot-split" directory is used to identify the peers. 'gemini'-style kvp's are used.

This routine uses the "FIFOPolicy" callback, and thus implements a "FIFO" First-In First-Out accounting policy. This is currently the only implemented policy; adding new policies should be 'easy'; read the source luke.

Definition at line 578 of file cap-gains.c.

00579 {
00580     Account *acc;
00581     gboolean splits_split_up = FALSE;
00582     GNCLot *lot;
00583     GNCPolicy *pcy;
00584 
00585     if (!split) return FALSE;
00586 
00587     /* If this split already belongs to a lot or the account doesn't
00588      * have lots, we are done.
00589      */
00590     if (split->lot) return FALSE;
00591     acc = split->acc;
00592     if (!xaccAccountHasTrades (acc))
00593         return FALSE;
00594     if (gnc_numeric_zero_p (split->amount))
00595         return FALSE;
00596 
00597     ENTER ("(split=%p)", split);
00598 
00599     pcy = gnc_account_get_policy(acc);
00600     xaccAccountBeginEdit (acc);
00601 
00602     /* If we are here, this split does not belong to any lot.
00603      * We ask the policy for a lot to assign it to.  This
00604      * block is written in the form of a while loop, since we
00605      * may have to bust a split across several lots.
00606      */
00607     while (split)
00608     {
00609         PINFO ("have split %p amount=%s", split,
00610                gnc_num_dbg_to_string (split->amount));
00611         split->gains |= GAINS_STATUS_VDIRTY;
00612         lot = pcy->PolicyGetLot (pcy, split);
00613         if (!lot)
00614         {
00615             lot = gnc_lot_make_default (acc);
00616             PINFO ("start new lot (%s)", gnc_lot_get_title(lot));
00617         }
00618         split = xaccSplitAssignToLot (split, lot);
00619         if (split) splits_split_up = TRUE;
00620     }
00621     xaccAccountCommitEdit (acc);
00622 
00623     LEAVE (" split_up=%d", splits_split_up);
00624     return splits_split_up;
00625 }

Split* xaccSplitAssignToLot ( Split split,
GNCLot lot 
)

The xaccSplitAssignToLot() routine will fit the indicated split into the indicated lot, with the goal of closing the lot, or at least bringing the lot balance closer to closure. (A closed lot has a balance of zero). To make this "fit", a variety of checks and actions are performed. First, the lot must be open, and the sign of the split amount must be opposite to the sign of the lot balance. The 'opposite-sign' requirement is so that inserting the split will cause the size of the lot to decrease. If the amount of the split is too small, or is just right to close the lot, the split is added, and NULL is returned. If the split is larger than the lot balance, the split will be divided into sub-splits, one of which is just right to close the lot. A pointer to the other sub-split will be returned.

If the split had to be broken up, kvp markup in the "/lot-split" directory is used to identify the peers. 'gemini'-style kvp's are used.

Definition at line 364 of file cap-gains.c.

00365 {
00366     Account *acc;
00367     gnc_numeric baln;
00368     int cmp;
00369     gboolean baln_is_positive, amt_is_positive;
00370 
00371     if (!lot) return split;
00372     if (!split) return NULL;
00373 
00374     /* If this split already belongs to a lot, we are done. */
00375     if (split->lot) return NULL;
00376 
00377     /* Anomolous situation; except for voided transactions,
00378      * we don't expect to see splits with no amount ..
00379      * unless they're gains splits, and we shouldn't see those.
00380      */
00381     if (gnc_numeric_zero_p (split->amount))
00382     {
00383         if (xaccTransGetVoidStatus(split->parent)) return NULL;
00384 
00385         PWARN ("split with zero amount; value=%s gflag=%x gsplit=%p",
00386                gnc_num_dbg_to_string (split->amount),
00387                split->gains,
00388                split->gains_split);
00389         if (split->gains_split)
00390         {
00391             PWARN ("gains amt=%s value=%s",
00392                    gnc_num_dbg_to_string (split->gains_split->amount),
00393                    gnc_num_dbg_to_string (split->gains_split->value));
00394         }
00395         return NULL;
00396     }
00397 
00398     /* If the lot is closed, we can't add anything to it */
00399     baln = gnc_lot_get_balance (lot);
00400     if (gnc_lot_is_closed (lot)) return split;
00401 
00402     /* If the lot balance is zero, but the lot is open, then
00403      * the lot is empty. Unconditionally add the split. */
00404     if (gnc_numeric_zero_p (baln))
00405     {
00406         acc = split->acc;
00407         xaccAccountBeginEdit (acc);
00408         gnc_lot_add_split (lot, split);
00409         PINFO ("added split to empty lot, new lot baln=%s (%s)",
00410                gnc_num_dbg_to_string (gnc_lot_get_balance(lot)),
00411                gnc_lot_get_title (lot));
00412         xaccAccountCommitEdit (acc);
00413         return NULL;
00414     }
00415 
00416     /* If the sign of the split is the same as the sign of the lot,
00417      * add the split, but complain about it ... none of the currently
00418      * implemented accounting policies should be giving us splits
00419      * that make lots larger.  One a lot is open, the FIFO/LIFO
00420      * policies should be working only to make the lot smaller.
00421      * We can remove teh warning emssage come the day we have
00422      * fancier policies.
00423      */
00424     baln_is_positive = gnc_numeric_positive_p (baln);
00425     amt_is_positive = gnc_numeric_positive_p (split->amount);
00426     if ((baln_is_positive && amt_is_positive) ||
00427             ((!baln_is_positive) && (!amt_is_positive)))
00428     {
00429         PWARN ("accounting policy gave us split that enlarges the lot!\n"
00430                "old lot baln=%s split amt=%s lot=%s",
00431                gnc_num_dbg_to_string (gnc_lot_get_balance(lot)),
00432                gnc_num_dbg_to_string (split->amount),
00433                gnc_lot_get_title (lot));
00434 
00435         acc = split->acc;
00436         xaccAccountBeginEdit (acc);
00437         gnc_lot_add_split (lot, split);
00438         xaccAccountCommitEdit (acc);
00439         return NULL;
00440     }
00441 
00442     /* If adding the split would make the lot balance change sign,
00443      * then we split the split into two pieces: one piece that will
00444      * bring the lot balance to zero, and another to be dealt with
00445      * later.  */
00446     cmp = gnc_numeric_compare (gnc_numeric_abs(split->amount),
00447                                gnc_numeric_abs(baln));
00448 
00449     PINFO ("found open lot with baln=%s (%s)", gnc_num_dbg_to_string (baln),
00450            gnc_lot_get_title (lot));
00451 
00452     /* cmp == -1 if amt < baln, ==0 if amt==baln */
00453     if (0 >= cmp)
00454     {
00455         acc = split->acc;
00456         xaccAccountBeginEdit (acc);
00457         gnc_lot_add_split (lot, split);
00458         PINFO ("simple added split to lot, new lot baln=%s",
00459                gnc_num_dbg_to_string (gnc_lot_get_balance(lot)));
00460         xaccAccountCommitEdit (acc);
00461         return NULL;
00462     }
00463 
00464     /* If we are here, then (cmp == +1 iff (amt > baln)) and we need
00465      * to split up the split into pieces. Do it. */
00466     {
00467         time_t now = time(0);
00468         Split * new_split;
00469         gnc_numeric amt_a, amt_b, amt_tot;
00470         gnc_numeric val_a, val_b, val_tot;
00471         gnc_numeric frac;
00472         Transaction *trans;
00473         Timespec ts;
00474 
00475         acc = split->acc;
00476         xaccAccountBeginEdit (acc);
00477         trans = split->parent;
00478         xaccTransBeginEdit (trans);
00479 
00480         amt_tot = split->amount;
00481         amt_a = gnc_numeric_neg (baln);
00482         amt_b = gnc_numeric_sub_fixed (amt_tot, amt_a);
00483 
00484         PINFO ("++++++++++++++ splitting split=%p into amt = %s + %s",
00485                split,
00486                gnc_num_dbg_to_string(amt_a),
00487                gnc_num_dbg_to_string(amt_b) );
00488 
00489         /* Compute the value so that it holds in the same proportion:
00490          * i.e. so that (amt_a / amt_tot) = (val_a / val_tot)
00491          */
00492         val_tot = split->value;
00493         frac = gnc_numeric_div (amt_a, amt_tot,
00494                                 GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE);
00495         val_a = gnc_numeric_mul (frac, val_tot,
00496                                  gnc_numeric_denom(val_tot),
00497                                  GNC_HOW_RND_ROUND | GNC_HOW_DENOM_EXACT);
00498 
00499         val_b = gnc_numeric_sub_fixed (val_tot, val_a);
00500         if (gnc_numeric_check(val_a))
00501         {
00502             PERR("Numeric overflow\n"
00503                  "Acct=%s Txn=%s\n"
00504                  "\tval_tot=%s amt_a=%s amt_tot=%s\n",
00505                  xaccAccountGetName(acc),
00506                  xaccTransGetDescription(trans),
00507                  gnc_num_dbg_to_string(val_tot),
00508                  gnc_num_dbg_to_string(amt_a),
00509                  gnc_num_dbg_to_string(amt_tot));
00510         }
00511 
00512         if (gnc_numeric_zero_p(val_a) || gnc_numeric_zero_p(val_b))
00513         {
00514             PERR ("Failed to split into two!");
00515         }
00516 
00517         PINFO ("split value is = %s = %s + %s",
00518                gnc_num_dbg_to_string(val_tot),
00519                gnc_num_dbg_to_string(val_a),
00520                gnc_num_dbg_to_string(val_b) );
00521 
00522         xaccSplitSetAmount (split, amt_a);
00523         xaccSplitSetValue (split, val_a);
00524 
00525         /* Adding this split will have the effect of closing this lot,
00526          * because the new balance should be precisely zero. */
00527         gnc_lot_add_split (lot, split);
00528 
00529         /* Put the remainder of the balance into a new split,
00530          * which is in other respects just a clone of this one. */
00531         new_split = xaccMallocSplit (qof_instance_get_book(acc));
00532 
00533         /* Copy most of the split attributes */
00534         xaccSplitSetMemo (new_split, xaccSplitGetMemo (split));
00535         xaccSplitSetAction (new_split, xaccSplitGetAction (split));
00536         xaccSplitSetReconcile (new_split, xaccSplitGetReconcile (split));
00537         ts = xaccSplitRetDateReconciledTS (split);
00538         xaccSplitSetDateReconciledTS (new_split, &ts);
00539 
00540         /* We do not copy the KVP tree, as it seems like a dangerous
00541          * thing to do.  If the user wants to access stuff in the 'old'
00542          * kvp tree from the 'new' split, they shoudl follow the
00543          * 'split-lot' pointers.  Yes, this is complicated, but what
00544          * else can one do ??
00545          */
00546         /* Add kvp markup to indicate that these two splits used
00547          * to be one before being 'split'
00548          */
00549         gnc_kvp_bag_add (split->inst.kvp_data, "lot-split", now,
00550                          "peer_guid", xaccSplitGetGUID (new_split),
00551                          NULL);
00552 
00553         gnc_kvp_bag_add (new_split->inst.kvp_data, "lot-split", now,
00554                          "peer_guid", xaccSplitGetGUID (split),
00555                          NULL);
00556 
00557         xaccAccountInsertSplit (acc, new_split);
00558         xaccTransAppendSplit (trans, new_split);
00559         /* Set the amount and value after the split is in the transaction
00560            so it can find the correct denominator to use.  Otherwise it
00561            uses 100000 which may cause an overflow in some of the tests
00562            in test-period */
00563         xaccSplitSetAmount (new_split, amt_b);
00564         xaccSplitSetValue (new_split, val_b);
00565         xaccTransCommitEdit (trans);
00566         xaccAccountCommitEdit (acc);
00567         return new_split;
00568     }
00569 }

void xaccSplitComputeCapGains ( Split split,
Account gain_acc 
)

The xaccSplitComputeCapGains() routine computes the cap gains or losses for the indicated split. The gains are placed into the 'gains_acct'. If the gains_acct is NULL, then the appropriate default account is used (and created, if needed).

To compute the gains, the split must belong to a lot. If the split is the 'opening split', i.e. the earliest split in the lot, then nothing is done, as there are no gains/losses (something must be bought *and* sold for there to be a gain/loss).

Note also: the 'amount' of the split must be of opposite sign, and must be equal to or smaller, than the 'amount' of the opening split; its an error otherwise. If the 'amount' of the split is less than the opening amount, the gains are pro-rated.

The xaccLotComputeCapGains() routine merely invokes the above on each split in the lot.

Definition at line 676 of file cap-gains.c.

00677 {
00678     SplitList *node;
00679     GNCLot *lot;
00680     GNCPolicy *pcy;
00681     gnc_commodity *currency = NULL;
00682     gnc_numeric zero = gnc_numeric_zero();
00683     gnc_numeric value = zero;
00684     gnc_numeric frac;
00685     gnc_numeric opening_amount, opening_value;
00686     gnc_numeric lot_amount, lot_value;
00687     gnc_commodity *opening_currency;
00688 
00689     if (!split) return;
00690     lot = split->lot;
00691     if (!lot) return;
00692     pcy = gnc_account_get_policy(gnc_lot_get_account(lot));
00693     currency = split->parent->common_currency;
00694 
00695     ENTER ("(split=%p gains=%p status=0x%x lot=%s)", split,
00696            split->gains_split, split->gains,
00697            kvp_frame_get_string (gnc_lot_get_slots (lot), "/title"));
00698 
00699     /* Make sure the status flags and pointers are initialized */
00700     xaccSplitDetermineGainStatus(split);
00701 
00702     /* Not possible to have gains if the transaction currency and
00703      * account commodity are identical. */
00704     if (gnc_commodity_equal (currency,
00705                              xaccAccountGetCommodity(split->acc)))
00706     {
00707         LEAVE ("Currency transfer, gains not possible, returning.");
00708         return;
00709     }
00710 
00711     if (pcy->PolicyIsOpeningSplit (pcy, lot, split))
00712     {
00713 #if MOVE_THIS_TO_A_DATA_INTEGRITY_SCRUBBER
00714         /* Check to make sure that this opening split doesn't
00715          * have a cap-gain transaction associated with it.
00716          * If it does, that's wrong, and we ruthlessly destroy it.
00717          * XXX Don't do this, it leads to infinite loops.
00718          * We need to scrub out errors like this elsewhere!
00719          */
00720         if (xaccSplitGetCapGainsSplit (split))
00721         {
00722             Split *gains_split = xaccSplitGetCapGainsSplit(split);
00723             Transaction *trans = gains_split->parent;
00724             PERR ("Opening Split must not have cap gains!!\n");
00725 
00726             xaccTransBeginEdit (trans);
00727             xaccTransDestroy (trans);
00728             xaccTransCommitEdit (trans);
00729         }
00730 #endif
00731         LEAVE ("Lot opening split, returning.");
00732         return;
00733     }
00734 
00735     if (safe_strcmp ("stock-split", xaccSplitGetType (split)) == 0)
00736     {
00737         LEAVE ("Stock split split, returning.");
00738         return;
00739     }
00740 
00741     if (GAINS_STATUS_GAINS & split->gains)
00742     {
00743         Split *s;
00744         PINFO ("split is a gains recording split, switch over");
00745         /* If this is the split that records the gains, then work with
00746          * the split that generates the gains.
00747          */
00748         /* split = xaccSplitGetCapGainsSplit (split); */
00749         s = split->gains_split;
00750 
00751         /* This should never be NULL, and if it is, and its matching
00752          * parent can't be found, then its a bug, and we should be
00753          * discarding this split.   But ... for now .. return.
00754          * XXX move appropriate actions to a 'scrub' routine'
00755          */
00756         if (!s)
00757         {
00758             PERR ("Bad gains-split pointer! .. trying to recover.");
00759             split->gains_split = xaccSplitGetCapGainsSplit (split);
00760             s = split->gains_split;
00761             if (!s) return;
00762 #if MOVE_THIS_TO_A_DATA_INTEGRITY_SCRUBBER
00763             xaccTransDestroy (trans);
00764 #endif
00765         }
00766         split = s;
00767     }
00768 
00769     /* Note: if the value of the 'opening' split(s) has changed,
00770      * then the cap gains are changed. So we need to check not
00771      * only if this split is dirty, but also the lot-opening splits. */
00772     for (node = gnc_lot_get_split_list(lot); node; node = node->next)
00773     {
00774         Split *s = node->data;
00775         if (pcy->PolicyIsOpeningSplit(pcy, lot, s))
00776         {
00777             if (GAINS_STATUS_UNKNOWN == s->gains) xaccSplitDetermineGainStatus (s);
00778             if (s->gains & GAINS_STATUS_VDIRTY)
00779             {
00780                 /* Force a recompute to occur */
00781                 split->gains |= GAINS_STATUS_VDIRTY;
00782                 break;
00783             }
00784         }
00785     }
00786 
00787     /* If it doesn't look like this split is 'dirty', then there's
00788      * nothing to do. Just return. */
00789     if ((FALSE == (split->gains & GAINS_STATUS_A_VDIRTY))  &&
00790             (split->gains_split) &&
00791             (FALSE == (split->gains_split->gains & GAINS_STATUS_A_VDIRTY)))
00792     {
00793         LEAVE ("split not dirty, returning");
00794         return;
00795     }
00796 
00797     /* Yow! If amount is zero, there's nothing to do! Amount-zero splits
00798      * may exist if users attempted to manually record gains. */
00799     if (gnc_numeric_zero_p (split->amount)) return;
00800 
00801     /* If we got to here, then the split or something related is
00802      * 'dirty' and the gains really do need to be recomputed.
00803      * So start working things. */
00804 
00805     /* Get the amount and value in this lot at the time of this transaction. */
00806     gnc_lot_get_balance_before (lot, split, &lot_amount, &lot_value);
00807 
00808     pcy->PolicyGetLotOpening (pcy, lot, &opening_amount, &opening_value,
00809                               &opening_currency);
00810 
00811     /* Check to make sure the lot-opening currency and this split
00812      * use the same currency */
00813     if (FALSE == gnc_commodity_equiv (currency, opening_currency))
00814     {
00815         /* OK, the purchase and the sale were made in different currencies.
00816          * I don't know how to compute cap gains for that.  This is not
00817          * an error. Just punt, silently.
00818          */
00819         LEAVE ("Can't compute gains, mismatched commodities!");
00820         return;
00821     }
00822 
00823     /* Opening amount should be larger (or equal) to current split,
00824      * and it should be of the opposite sign.
00825      * XXX This should really be a part of a scrub routine that
00826      * cleans up the lot, before we get at it!
00827      */
00828     if (0 > gnc_numeric_compare (gnc_numeric_abs(lot_amount),
00829                                  gnc_numeric_abs(split->amount)))
00830     {
00831         GList *n;
00832         for (n = gnc_lot_get_split_list(lot); n; n = n->next)
00833         {
00834             Split *s = n->data;
00835             PINFO ("split amt=%s", gnc_num_dbg_to_string(s->amount));
00836         }
00837         PERR ("Malformed Lot \"%s\"! (too thin!) "
00838               "opening amt=%s split amt=%s baln=%s",
00839               gnc_lot_get_title (lot),
00840               gnc_num_dbg_to_string (lot_amount),
00841               gnc_num_dbg_to_string (split->amount),
00842               gnc_num_dbg_to_string (gnc_lot_get_balance(lot)));
00843         return;
00844     }
00845     if ( (gnc_numeric_negative_p(lot_amount) ||
00846             gnc_numeric_positive_p(split->amount)) &&
00847             (gnc_numeric_positive_p(lot_amount) ||
00848              gnc_numeric_negative_p(split->amount)))
00849     {
00850         GList *n;
00851         for (n = gnc_lot_get_split_list(lot); n; n = n->next)
00852         {
00853             Split *s = n->data;
00854             PINFO ("split amt=%s", gnc_num_dbg_to_string(s->amount));
00855         }
00856         PERR ("Malformed Lot \"%s\"! (too fat!) "
00857               "opening amt=%s split amt=%s baln=%s",
00858               gnc_lot_get_title (lot),
00859               gnc_num_dbg_to_string (lot_amount),
00860               gnc_num_dbg_to_string (split->amount),
00861               gnc_num_dbg_to_string (gnc_lot_get_balance(lot)));
00862         return;
00863     }
00864 
00865     /* The cap gains is the difference between the basis prior to the
00866      * current split, and the current split, pro-rated for an equal
00867      * amount of shares.
00868      * i.e. purchase_price = lot_value / lot_amount
00869      * cost_basis = purchase_price * current_split_amount
00870      * cap_gain = current_split_value - cost_basis
00871      */
00872     /* Fraction of the lot that this split represents: */
00873     frac = gnc_numeric_div (split->amount, lot_amount,
00874                             GNC_DENOM_AUTO,
00875                             GNC_HOW_DENOM_REDUCE);
00876     /* Basis for this split: */
00877     value = gnc_numeric_mul (frac, lot_value,
00878                              gnc_numeric_denom(opening_value),
00879                              GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND);
00880     /* Capital gain for this split: */
00881     value = gnc_numeric_sub (value, split->value,
00882                              GNC_DENOM_AUTO, GNC_HOW_DENOM_FIXED);
00883     PINFO ("Open amt=%s val=%s;  split amt=%s val=%s; gains=%s\n",
00884            gnc_num_dbg_to_string (lot_amount),
00885            gnc_num_dbg_to_string (lot_value),
00886            gnc_num_dbg_to_string (split->amount),
00887            gnc_num_dbg_to_string (split->value),
00888            gnc_num_dbg_to_string (value));
00889     if (gnc_numeric_check (value))
00890     {
00891         PERR ("Numeric overflow during gains calculation\n"
00892               "Acct=%s Txn=%s\n"
00893               "\tOpen amt=%s val=%s\n\tsplit amt=%s val=%s\n\tgains=%s\n",
00894               xaccAccountGetName(split->acc),
00895               xaccTransGetDescription(split->parent),
00896               gnc_num_dbg_to_string (lot_amount),
00897               gnc_num_dbg_to_string (lot_value),
00898               gnc_num_dbg_to_string (split->amount),
00899               gnc_num_dbg_to_string (split->value),
00900               gnc_num_dbg_to_string (value));
00901         return;
00902     }
00903 
00904     /* Are the cap gains zero?  If not, add a balancing transaction.
00905      * As per design doc lots.txt: the transaction has two splits,
00906      * with equal & opposite values.  The amt of one iz zero (so as
00907      * not to upset the lot balance), the amt of the other is the same
00908      * as its value (its the realized gain/loss).
00909      */
00910     if (FALSE == gnc_numeric_zero_p (value))
00911     {
00912         Transaction *trans;
00913         Split *lot_split, *gain_split;
00914         Timespec ts;
00915         gboolean new_gain_split;
00916         gnc_numeric negvalue = gnc_numeric_neg (value);
00917 
00918         /* See if there already is an associated gains transaction.
00919          * If there is, adjust its value as appropriate. Else, create
00920          * a new gains transaction.
00921          */
00922         /* lot_split = xaccSplitGetCapGainsSplit (split);  */
00923         lot_split = split->gains_split;
00924 
00925         if (NULL == lot_split)
00926         {
00927             Account *lot_acc = gnc_lot_get_account(lot);
00928             QofBook *book = qof_instance_get_book(lot_acc);
00929 
00930             new_gain_split = TRUE;
00931 
00932             lot_split = xaccMallocSplit (book);
00933             gain_split = xaccMallocSplit (book);
00934 
00935             /* Check to make sure the gains account currency matches. */
00936             if ((NULL == gain_acc) ||
00937                     (FALSE == gnc_commodity_equiv (currency,
00938                                                    xaccAccountGetCommodity(gain_acc))))
00939             {
00940                 gain_acc = GetOrMakeGainAcct (lot_acc, currency);
00941             }
00942 
00943             xaccAccountBeginEdit (gain_acc);
00944             xaccAccountInsertSplit (gain_acc, gain_split);
00945             xaccAccountCommitEdit (gain_acc);
00946 
00947             xaccAccountBeginEdit (lot_acc);
00948             xaccAccountInsertSplit (lot_acc, lot_split);
00949             xaccAccountCommitEdit (lot_acc);
00950 
00951             trans = xaccMallocTransaction (book);
00952 
00953             xaccTransBeginEdit (trans);
00954             xaccTransSetCurrency (trans, currency);
00955             xaccTransSetDescription (trans, _("Realized Gain/Loss"));
00956 
00957             xaccTransAppendSplit (trans, lot_split);
00958             xaccTransAppendSplit (trans, gain_split);
00959 
00960             xaccSplitSetMemo (lot_split, _("Realized Gain/Loss"));
00961             xaccSplitSetMemo (gain_split, _("Realized Gain/Loss"));
00962 
00963             /* For the new transaction, install KVP markup indicating
00964              * that this is the gains transaction that corresponds
00965              * to the gains source.
00966              */
00967             kvp_frame_set_guid (split->inst.kvp_data, "gains-split",
00968                                 xaccSplitGetGUID (lot_split));
00969             kvp_frame_set_guid (lot_split->inst.kvp_data, "gains-source",
00970                                 xaccSplitGetGUID (split));
00971 
00972         }
00973         else
00974         {
00975             trans = lot_split->parent;
00976             gain_split = xaccSplitGetOtherSplit (lot_split);
00977             /* If the gain is already recorded corectly do nothing.  This is
00978              * more than just an optimization since this may be called during
00979              * gnc_book_partition_txn and depending on the order in which things
00980              * happen some splits may be in the wrong book at that time. */
00981             if (split->gains_split == lot_split &&
00982                     lot_split->gains_split == split &&
00983                     gain_split->gains_split == split &&
00984                     gnc_numeric_equal (xaccSplitGetValue (lot_split), value) &&
00985                     gnc_numeric_zero_p (xaccSplitGetAmount (lot_split)) &&
00986                     gnc_numeric_equal (xaccSplitGetValue (gain_split), negvalue) &&
00987                     gnc_numeric_equal (xaccSplitGetAmount (gain_split), negvalue))
00988             {
00989                 new_gain_split = FALSE;
00990             }
00991             else
00992             {
00993                 new_gain_split = TRUE;
00994                 xaccTransBeginEdit (trans);
00995 
00996                 /* Make sure the existing gains trans has the correct currency,
00997                  * just in case someone screwed with it! */
00998                 if (FALSE == gnc_commodity_equiv(currency, trans->common_currency))
00999                 {
01000                     PWARN ("Resetting the transaction currency!");
01001                     xaccTransSetCurrency (trans, currency);
01002                 }
01003             }
01004         }
01005 
01006         if (new_gain_split)
01007         {
01008             /* Common to both */
01009             ts = xaccTransRetDatePostedTS (split->parent);
01010             xaccTransSetDatePostedTS (trans, &ts);
01011             xaccTransSetDateEnteredSecs (trans, time(0));
01012 
01013             xaccSplitSetAmount (lot_split, zero);
01014             xaccSplitSetValue (lot_split, value);
01015 
01016             xaccSplitSetAmount (gain_split, negvalue);
01017             xaccSplitSetValue (gain_split, negvalue);
01018 
01019             /* Some short-cuts to help avoid the above kvp lookup. */
01020             split->gains = GAINS_STATUS_CLEAN;
01021             split->gains_split = lot_split;
01022             lot_split->gains = GAINS_STATUS_GAINS;
01023             lot_split->gains_split = split;
01024             gain_split->gains = GAINS_STATUS_GAINS;
01025             gain_split->gains_split = split;
01026 
01027             /* Do this last since it may generate an event that will call us
01028                recursively. */
01029             gnc_lot_add_split (lot, lot_split);
01030 
01031             xaccTransCommitEdit (trans);
01032         }
01033     }
01034     LEAVE ("(lot=%s)", gnc_lot_get_title(lot));
01035 }

gnc_numeric xaccSplitGetCapGains ( Split  ) 

The xaccSplitGetCapGains() method returns the value of capital gains (if any) associated with the indicated split. In order for there to be any capital gains, several things must hold true about this split: (1) It must have been involved in trading (for aexample, by belonging to a stock or trading account) (2) It must have been assigned to a lot. (3) It cannot be the opening split of a lot; that is, it must be a matching sale of an earlier purchase (or vice versa).

Definition at line 1040 of file cap-gains.c.

01041 {
01042     if (!split) return gnc_numeric_zero();
01043     ENTER("(split=%p)", split);
01044 
01045     if (GAINS_STATUS_UNKNOWN == split->gains)
01046         xaccSplitDetermineGainStatus(split);
01047     if ((split->gains & GAINS_STATUS_A_VDIRTY) ||
01048             (split->gains_split &&
01049              (split->gains_split->gains & GAINS_STATUS_A_VDIRTY)))
01050     {
01051         xaccSplitComputeCapGains (split, NULL);
01052     }
01053 
01054     /* If this is the source split, get the gains from the one
01055      * that records the gains.  If this already is the gains split,
01056      * its a no-op. */
01057     if (!(GAINS_STATUS_GAINS & split->gains))
01058     {
01059         /* split = xaccSplitGetCapGainsSplit (split); */
01060         split = split->gains_split;
01061     }
01062 
01063     LEAVE("(split=%p)", split);
01064     if (!split) return gnc_numeric_zero();
01065 
01066     return split->value;
01067 }

Split* xaccSplitGetCapGainsSplit ( const Split  ) 

The xaccSplitGetCapGainsSplit() routine returns the split that records the cap gains for this split. It returns NULL if not found. This routine does nothing more than search for the split recorded in the KVP key "/gains-split"

Definition at line 630 of file cap-gains.c.

00631 {
00632     KvpValue *val;
00633     GUID *gains_guid;
00634     Split *gains_split;
00635 
00636     if (!split) return NULL;
00637 
00638     val = kvp_frame_get_slot (split->inst.kvp_data, "gains-split");
00639     if (!val) return NULL;
00640     gains_guid = kvp_value_get_guid (val);
00641     if (!gains_guid) return NULL;
00642 
00643     /* Both splits will be in the same collection, so search there. */
00644     gains_split = (Split*) qof_collection_lookup_entity (
00645                       qof_instance_get_collection(split), gains_guid);
00646     PINFO ("split=%p has gains-split=%p", split, gains_split);
00647     return gains_split;
00648 }

Split* xaccSplitGetGainsSourceSplit ( const Split  ) 

The xaccSplitGetGainsSourceSplit() routine returns the split that is the source of the cap gains in this split. It returns NULL if not found. This routine does nothing more than search for the split recorded in the KVP key "/gains-source"

Definition at line 653 of file cap-gains.c.

00654 {
00655     KvpValue *val;
00656     GUID *source_guid;
00657     Split *source_split;
00658 
00659     if (!split) return NULL;
00660 
00661     val = kvp_frame_get_slot (split->inst.kvp_data, "gains-source");
00662     if (!val) return NULL;
00663     source_guid = kvp_value_get_guid (val);
00664     if (!source_guid) return NULL;
00665 
00666     /* Both splits will be in the same collection, so search there. */
00667     source_split = (Split*) qof_collection_lookup_entity(
00668                        qof_instance_get_collection(split), source_guid);
00669     PINFO ("split=%p has source-split=%p", split, source_split);
00670     return source_split;
00671 }


Generated on Thu Mar 18 04:44:41 2010 for GnuCash by  doxygen 1.5.7.1