Files | |
| file | cap-gains.h |
| Utilities to Automatically Compute Capital Gains/Losses. | |
Functions | |
| gnc_numeric | xaccSplitGetCapGains (Split *) |
| gboolean | xaccAccountHasTrades (const Account *) |
| GNCLot * | xaccAccountFindEarliestOpenLot (Account *acc, gnc_numeric sign, gnc_commodity *currency) |
| GNCLot * | xaccAccountFindLatestOpenLot (Account *acc, gnc_numeric sign, gnc_commodity *currency) |
| Account * | xaccAccountGetDefaultGainAccount (const Account *acc, const gnc_commodity *currency) |
| void | xaccAccountSetDefaultGainAccount (Account *acc, const Account *gains_acct) |
| Split * | xaccSplitGetCapGainsSplit (const Split *) |
| Split * | xaccSplitGetGainsSourceSplit (const Split *) |
| gboolean | xaccSplitAssign (Split *split) |
| Split * | xaccSplitAssignToLot (Split *split, GNCLot *lot) |
| void | xaccSplitComputeCapGains (Split *split, Account *gain_acc) |
| void | xaccLotComputeCapGains (GNCLot *lot, Account *gain_acc) |
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.
| 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 }
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 }
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 }
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 }
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 }
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 }
1.5.7.1