cap-gains.c

Go to the documentation of this file.
00001 /********************************************************************\
00002  * cap-gains.c -- Automatically Compute Capital Gains/Losses        *
00003  *                                                                  *
00004  * This program is free software; you can redistribute it and/or    *
00005  * modify it under the terms of the GNU General Public License as   *
00006  * published by the Free Software Foundation; either version 2 of   *
00007  * the License, or (at your option) any later version.              *
00008  *                                                                  *
00009  * This program is distributed in the hope that it will be useful,  *
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
00012  * GNU General Public License for more details.                     *
00013  *                                                                  *
00014  * You should have received a copy of the GNU General Public License*
00015  * along with this program; if not, contact:                        *
00016  *                                                                  *
00017  * Free Software Foundation           Voice:  +1-617-542-5942       *
00018  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
00019  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
00020 \********************************************************************/
00021 
00056 #include "config.h"
00057 
00058 #include <glib.h>
00059 #include <glib/gi18n.h>
00060 
00061 #include "AccountP.h"
00062 #include "Scrub2.h"
00063 #include "Scrub3.h"
00064 #include "Transaction.h"
00065 #include "TransactionP.h"
00066 #include "cap-gains.h"
00067 #include "gnc-engine.h"
00068 #include "gnc-lot.h"
00069 #include "gnc-lot-p.h"
00070 #include "policy.h"
00071 #include "policy-p.h"
00072 
00073 static QofLogModule log_module = GNC_MOD_LOT;
00074 
00075 
00076 /* ============================================================== */
00077 
00078 gboolean 
00079 xaccAccountHasTrades (const Account *acc)
00080 {
00081    gnc_commodity *acc_comm;
00082    SplitList *splits, *node;
00083 
00084    if (!acc) return FALSE;
00085 
00086    if (xaccAccountIsPriced (acc))
00087       return TRUE;
00088       
00089    acc_comm = xaccAccountGetCommodity(acc);
00090 
00091    splits = xaccAccountGetSplitList(acc);
00092    for (node=splits; node; node=node->next)
00093    {
00094       Split *s = node->data;
00095       Transaction *t = s->parent;
00096       if (acc_comm != t->common_currency) return TRUE;
00097    }
00098 
00099    return FALSE;
00100 }
00101 
00102 /* ============================================================== */
00103 
00104 struct find_lot_s
00105 {
00106    GNCLot *lot;
00107    gnc_commodity *currency;
00108    Timespec ts;
00109    int (*numeric_pred)(gnc_numeric);
00110    gboolean (*date_pred)(Timespec e, Timespec tr);
00111 };
00112 
00113 static gboolean 
00114 earliest_pred (Timespec earl, Timespec tran)
00115 {
00116   return ((earl.tv_sec > tran.tv_sec)  ||
00117        ((earl.tv_sec == tran.tv_sec) && (earl.tv_nsec > tran.tv_nsec)));
00118 }
00119 
00120 static gboolean 
00121 latest_pred (Timespec earl, Timespec tran)
00122 {
00123   return ((earl.tv_sec < tran.tv_sec)  ||
00124        ((earl.tv_sec == tran.tv_sec) && (earl.tv_nsec < tran.tv_nsec)));
00125 }
00126 
00127 static gpointer 
00128 finder_helper (GNCLot *lot,  gpointer user_data)
00129 {
00130    struct find_lot_s *els = user_data;
00131    Split *s;
00132    Transaction *trans;
00133    gnc_numeric bal;
00134    gboolean opening_is_positive, bal_is_positive;
00135 
00136    if (gnc_lot_is_closed (lot)) return NULL;
00137 
00138    s = gnc_lot_get_earliest_split (lot);
00139    if (s == NULL) return NULL;
00140    
00141    /* We want a lot whose balance is of the correct sign.  All splits
00142       in a lot must be the opposite sign of the opening split.  We also
00143       want to ignore lots that are overfull, i.e., where the balance in
00144       the lot is of opposite sign to the opening split in the lot. */
00145    if (0 == (els->numeric_pred) (s->amount)) return NULL;
00146    bal = gnc_lot_get_balance (lot);
00147    opening_is_positive = gnc_numeric_positive_p (s->amount);
00148    bal_is_positive = gnc_numeric_positive_p (bal);
00149    if (opening_is_positive != bal_is_positive) return NULL;
00150    
00151    trans = s->parent;
00152    if (els->currency && 
00153        (FALSE == gnc_commodity_equiv (els->currency,
00154                                       trans->common_currency)))
00155    {
00156       return NULL;
00157    }
00158 
00159    if (els->date_pred (els->ts, trans->date_posted))
00160    {
00161       els->ts = trans->date_posted;
00162       els->lot = lot;
00163    }
00164    
00165    return NULL;
00166 }
00167 
00168 static inline GNCLot *
00169 xaccAccountFindOpenLot (Account *acc, gnc_numeric sign, 
00170    gnc_commodity *currency,
00171    gint64 guess,
00172    gboolean (*date_pred)(Timespec, Timespec))
00173 {
00174    struct find_lot_s es;
00175 
00176    es.lot = NULL;
00177    es.currency = currency;
00178    es.ts.tv_sec = guess;
00179    es.ts.tv_nsec = 0;
00180    es.date_pred = date_pred;
00181 
00182    if (gnc_numeric_positive_p(sign)) es.numeric_pred = gnc_numeric_negative_p;
00183    else es.numeric_pred = gnc_numeric_positive_p;
00184       
00185    xaccAccountForEachLot (acc, finder_helper, &es);
00186    return es.lot;
00187 }
00188 
00189 GNCLot *
00190 xaccAccountFindEarliestOpenLot (Account *acc, gnc_numeric sign, 
00191                                 gnc_commodity *currency)
00192 {
00193    GNCLot *lot;
00194    ENTER (" sign=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT, sign.num, 
00195           sign.denom);
00196       
00197    lot = xaccAccountFindOpenLot (acc, sign, currency,
00198                    G_MAXINT64, earliest_pred);
00199    LEAVE ("found lot=%p %s baln=%s", lot, gnc_lot_get_title (lot),
00200                gnc_num_dbg_to_string(gnc_lot_get_balance(lot)));
00201    return lot;
00202 }
00203 
00204 GNCLot *
00205 xaccAccountFindLatestOpenLot (Account *acc, gnc_numeric sign,
00206                               gnc_commodity *currency)
00207 {
00208    GNCLot *lot;
00209    ENTER (" sign=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT,
00210           sign.num, sign.denom);
00211       
00212    lot = xaccAccountFindOpenLot (acc, sign, currency,
00213                    G_MININT64, latest_pred);
00214    LEAVE ("found lot=%p %s", lot, gnc_lot_get_title (lot));
00215    return lot;
00216 }
00217 
00218 /* ============================================================== */
00219 /* Similar to GetOrMakeAccount, but different in important ways */
00220 
00221 static Account *
00222 GetOrMakeLotOrphanAccount (Account *root, gnc_commodity * currency)
00223 {
00224   char * accname;
00225   Account * acc;
00226 
00227   g_return_val_if_fail (root, NULL);
00228 
00229   /* build the account name */
00230   if (!currency)
00231   {
00232     PERR ("No currency specified!");
00233     return NULL;
00234   }
00235 
00236   accname = g_strconcat (_("Orphaned Gains"), "-",
00237                          gnc_commodity_get_mnemonic (currency), NULL);
00238 
00239   /* See if we've got one of these going already ... */
00240   acc = gnc_account_lookup_by_name(root, accname);
00241 
00242   if (acc == NULL)
00243   {
00244     /* Guess not. We'll have to build one. */
00245     acc = xaccMallocAccount (gnc_account_get_book(root));
00246     xaccAccountBeginEdit (acc);
00247     xaccAccountSetName (acc, accname);
00248     xaccAccountSetCommodity (acc, currency);
00249     xaccAccountSetType (acc, ACCT_TYPE_INCOME);
00250     xaccAccountSetDescription (acc, _("Realized Gain/Loss"));
00251     xaccAccountSetNotes (acc, 
00252          _("Realized Gains or Losses from "
00253            "Commodity or Trading Accounts "
00254            "that haven't been recorded elsewhere."));
00255 
00256     /* Hang the account off the root. */
00257     gnc_account_append_child (root, acc);
00258     xaccAccountCommitEdit (acc);
00259   }
00260 
00261   g_free (accname);
00262 
00263   return acc;
00264 }
00265 
00266 /* ============================================================== */
00267 
00268 void
00269 xaccAccountSetDefaultGainAccount (Account *acc, const Account *gain_acct)
00270 {
00271   KvpFrame *cwd;
00272   KvpValue *vvv;
00273   const char * cur_name;
00274   gnc_commodity *acc_comm;
00275 
00276   if (!acc || !gain_acct) return;
00277 
00278   cwd = xaccAccountGetSlots (acc);
00279   cwd = kvp_frame_get_frame_slash (cwd, "/lot-mgmt/gains-act/");
00280 
00281   /* Accounts are indexed by thier unique currency name */
00282   acc_comm = xaccAccountGetCommodity(acc);
00283   cur_name = gnc_commodity_get_unique_name (acc_comm);
00284 
00285   xaccAccountBeginEdit (acc);
00286   vvv = kvp_value_new_guid (xaccAccountGetGUID (gain_acct));
00287   kvp_frame_set_slot_nc (cwd, cur_name, vvv);
00288   qof_instance_set_slots(QOF_INSTANCE(acc), acc->inst.kvp_data);
00289   xaccAccountCommitEdit (acc);
00290 }
00291 
00292 /* ============================================================== */
00293 
00294 Account *
00295 xaccAccountGetDefaultGainAccount (const Account *acc, const gnc_commodity * currency)
00296 {
00297   Account *gain_acct = NULL;
00298   KvpFrame *cwd;
00299   KvpValue *vvv;
00300   GUID * gain_acct_guid;
00301   const char * cur_name;
00302 
00303   if (!acc || !currency) return NULL;
00304 
00305   cwd = xaccAccountGetSlots (acc);
00306   cwd = kvp_frame_get_frame_slash (cwd, "/lot-mgmt/gains-act/");
00307 
00308   /* Accounts are indexed by thier unique currency name */
00309   cur_name = gnc_commodity_get_unique_name (currency);
00310   vvv = kvp_frame_get_slot (cwd, cur_name);
00311   gain_acct_guid = kvp_value_get_guid (vvv);
00312 
00313   gain_acct = xaccAccountLookup (gain_acct_guid, qof_instance_get_book(acc));
00314   return gain_acct;
00315 }
00316 
00317 /* ============================================================== */
00318 /* Functionally identical to the following:
00319  *   if (!xaccAccountGetDefaultGainAccount()) {
00320  *               xaccAccountSetDefaultGainAccount (); }
00321  * except that it saves a few cycles.
00322  */
00323 
00324 static Account *
00325 GetOrMakeGainAcct (Account *acc, gnc_commodity * currency)
00326 {
00327   Account *gain_acct = NULL;
00328   KvpFrame *cwd;
00329   KvpValue *vvv;
00330   GUID * gain_acct_guid;
00331   const char * cur_name;
00332 
00333   cwd = xaccAccountGetSlots (acc);
00334   cwd = kvp_frame_get_frame_slash (cwd, "/lot-mgmt/gains-act/");
00335 
00336   /* Accounts are indexed by thier unique currency name */
00337   cur_name = gnc_commodity_get_unique_name (currency);
00338   vvv = kvp_frame_get_slot (cwd, cur_name);
00339   gain_acct_guid = kvp_value_get_guid (vvv);
00340 
00341   gain_acct = xaccAccountLookup (gain_acct_guid, qof_instance_get_book(acc));
00342 
00343   /* If there is no default place to put gains/losses 
00344    * for this account, then create such a place */
00345   if (NULL == gain_acct)
00346   {
00347       Account *root;
00348 
00349       xaccAccountBeginEdit (acc);
00350       root = gnc_account_get_root(acc);
00351       gain_acct = GetOrMakeLotOrphanAccount (root, currency);
00352 
00353       vvv = kvp_value_new_guid (xaccAccountGetGUID (gain_acct));
00354       kvp_frame_set_slot_nc (cwd, cur_name, vvv);
00355       qof_instance_set_slots(QOF_INSTANCE(acc), acc->inst.kvp_data);
00356       xaccAccountCommitEdit (acc);
00357 
00358   }
00359   return gain_acct;
00360 }
00361 
00362 /* ============================================================== */
00363 
00364 Split *
00365 xaccSplitAssignToLot (Split *split, GNCLot *lot)
00366 {
00367    Account *acc;
00368    gnc_numeric baln;
00369    int cmp;
00370    gboolean baln_is_positive, amt_is_positive;
00371 
00372    if (!lot) return split;
00373    if (!split) return NULL;
00374 
00375    /* If this split already belongs to a lot, we are done. */
00376    if (split->lot) return NULL;
00377 
00378    /* Anomolous situation; except for voided transactions, 
00379     * we don't expect to see splits with no amount .. 
00380     * unless they're gains splits, and we shouldn't see those. 
00381     */
00382    if (gnc_numeric_zero_p (split->amount))
00383    {
00384      if (xaccTransGetVoidStatus(split->parent)) return NULL;
00385 
00386      PWARN ("split with zero amount; value=%s gflag=%x gsplit=%p",
00387             gnc_num_dbg_to_string (split->amount), 
00388             split->gains,
00389             split->gains_split);
00390      if (split->gains_split)
00391      {
00392        PWARN ("gains amt=%s value=%s", 
00393               gnc_num_dbg_to_string (split->gains_split->amount),
00394               gnc_num_dbg_to_string (split->gains_split->value));
00395      }
00396      return NULL;
00397    }
00398 
00399    /* If the lot is closed, we can't add anything to it */
00400    baln = gnc_lot_get_balance (lot);
00401    if (gnc_lot_is_closed (lot)) return split;
00402 
00403    /* If the lot balance is zero, but the lot is open, then 
00404     * the lot is empty. Unconditionally add the split. */
00405    if (gnc_numeric_zero_p (baln))
00406    {
00407       acc = split->acc;
00408       xaccAccountBeginEdit (acc);
00409       gnc_lot_add_split (lot, split);
00410       PINFO ("added split to empty lot, new lot baln=%s (%s)", 
00411            gnc_num_dbg_to_string (gnc_lot_get_balance(lot)),
00412            gnc_lot_get_title (lot));
00413       xaccAccountCommitEdit (acc);
00414       return NULL;
00415    }
00416 
00417    /* If the sign of the split is the same as the sign of the lot,
00418     * add the split, but complain about it ... none of the currently
00419     * implemented accounting policies should be giving us splits 
00420     * that make lots larger.  One a lot is open, the FIFO/LIFO
00421     * policies should be working only to make the lot smaller.
00422     * We can remove teh warning emssage come the day we have 
00423     * fancier policies.
00424     */
00425    baln_is_positive = gnc_numeric_positive_p (baln);
00426    amt_is_positive = gnc_numeric_positive_p (split->amount);
00427    if ((baln_is_positive && amt_is_positive) ||
00428        ((!baln_is_positive) && (!amt_is_positive)))
00429    {
00430       PWARN ("accounting policy gave us split that enlarges the lot!\n"
00431              "old lot baln=%s split amt=%s lot=%s",
00432              gnc_num_dbg_to_string (gnc_lot_get_balance(lot)),
00433              gnc_num_dbg_to_string (split->amount),
00434              gnc_lot_get_title (lot));
00435 
00436       acc = split->acc;
00437       xaccAccountBeginEdit (acc);
00438       gnc_lot_add_split (lot, split);
00439       xaccAccountCommitEdit (acc);
00440       return NULL;
00441    }
00442 
00443    /* If adding the split would make the lot balance change sign,
00444     * then we split the split into two pieces: one piece that will
00445     * bring the lot balance to zero, and another to be dealt with
00446     * later.  */
00447    cmp = gnc_numeric_compare (gnc_numeric_abs(split->amount),
00448                                         gnc_numeric_abs(baln));
00449 
00450    PINFO ("found open lot with baln=%s (%s)", gnc_num_dbg_to_string (baln),
00451                                               gnc_lot_get_title (lot));
00452 
00453    /* cmp == -1 if amt < baln, ==0 if amt==baln */
00454    if (0 >= cmp) 
00455    {
00456       acc = split->acc;
00457       xaccAccountBeginEdit (acc);
00458       gnc_lot_add_split (lot, split);
00459       PINFO ("simple added split to lot, new lot baln=%s", 
00460            gnc_num_dbg_to_string (gnc_lot_get_balance(lot)));
00461       xaccAccountCommitEdit (acc);
00462       return NULL;
00463    }
00464 
00465    /* If we are here, then (cmp == +1 iff (amt > baln)) and we need
00466     * to split up the split into pieces. Do it. */
00467    {
00468       time_t now = time(0);
00469       Split * new_split;
00470       gnc_numeric amt_a, amt_b, amt_tot;
00471       gnc_numeric val_a, val_b, val_tot;
00472       gnc_numeric frac;
00473       Transaction *trans;
00474       Timespec ts;
00475 
00476       acc = split->acc;
00477       xaccAccountBeginEdit (acc);
00478       trans = split->parent;
00479       xaccTransBeginEdit (trans);
00480 
00481       amt_tot = split->amount;
00482       amt_a = gnc_numeric_neg (baln);
00483       amt_b = gnc_numeric_sub_fixed (amt_tot, amt_a);
00484 
00485       PINFO ("++++++++++++++ splitting split=%p into amt = %s + %s",
00486               split,
00487               gnc_num_dbg_to_string(amt_a),
00488               gnc_num_dbg_to_string(amt_b) );
00489 
00490       /* Compute the value so that it holds in the same proportion:
00491        * i.e. so that (amt_a / amt_tot) = (val_a / val_tot)
00492        */
00493       val_tot = split->value;
00494       frac = gnc_numeric_div (amt_a, amt_tot, 
00495                         GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE);
00496       val_a = gnc_numeric_mul (frac, val_tot, 
00497                         gnc_numeric_denom(val_tot), 
00498                         GNC_HOW_RND_ROUND| GNC_HOW_DENOM_EXACT);
00499  
00500       val_b = gnc_numeric_sub_fixed (val_tot, val_a);
00501       if (gnc_numeric_check(val_a))
00502       {
00503          PERR("Numeric overflow\n"
00504               "Acct=%s Txn=%s\n"
00505               "\tval_tot=%s amt_a=%s amt_tot=%s\n",
00506               xaccAccountGetName(acc),
00507               xaccTransGetDescription(trans),
00508               gnc_num_dbg_to_string(val_tot),
00509               gnc_num_dbg_to_string(amt_a),
00510               gnc_num_dbg_to_string(amt_tot));
00511       }
00512 
00513       if (gnc_numeric_zero_p(val_a) || gnc_numeric_zero_p(val_b))
00514       {
00515          PERR ("Failed to split into two!");
00516       }
00517 
00518       PINFO ("split value is = %s = %s + %s",
00519               gnc_num_dbg_to_string(val_tot),
00520               gnc_num_dbg_to_string(val_a),
00521               gnc_num_dbg_to_string(val_b) );
00522 
00523       xaccSplitSetAmount (split, amt_a);
00524       xaccSplitSetValue (split, val_a);
00525 
00526       /* Adding this split will have the effect of closing this lot,
00527        * because the new balance should be precisely zero. */
00528       gnc_lot_add_split (lot, split);
00529 
00530       /* Put the remainder of the balance into a new split, 
00531        * which is in other respects just a clone of this one. */
00532       new_split = xaccMallocSplit (qof_instance_get_book(acc));
00533 
00534       /* Copy most of the split attributes */
00535       xaccSplitSetMemo (new_split, xaccSplitGetMemo (split));
00536       xaccSplitSetAction (new_split, xaccSplitGetAction (split));
00537       xaccSplitSetReconcile (new_split, xaccSplitGetReconcile (split));
00538       ts = xaccSplitRetDateReconciledTS (split);
00539       xaccSplitSetDateReconciledTS (new_split, &ts);
00540 
00541       /* We do not copy the KVP tree, as it seems like a dangerous
00542        * thing to do.  If the user wants to access stuff in the 'old'
00543        * kvp tree from the 'new' split, they shoudl follow the 
00544        * 'split-lot' pointers.  Yes, this is complicated, but what
00545        * else can one do ??
00546        */
00547       /* Add kvp markup to indicate that these two splits used 
00548        * to be one before being 'split' 
00549        */
00550       gnc_kvp_bag_add (split->inst.kvp_data, "lot-split", now, 
00551                        "peer_guid", xaccSplitGetGUID (new_split), 
00552                        NULL);
00553 
00554       gnc_kvp_bag_add (new_split->inst.kvp_data, "lot-split", now, 
00555                        "peer_guid", xaccSplitGetGUID (split), 
00556                        NULL);
00557       
00558       xaccAccountInsertSplit (acc, new_split);
00559       xaccTransAppendSplit (trans, new_split);
00560       /* Set the amount and value after the split is in the transaction
00561          so it can find the correct denominator to use.  Otherwise it 
00562          uses 100000 which may cause an overflow in some of the tests
00563          in test-period */
00564       xaccSplitSetAmount (new_split, amt_b);
00565       xaccSplitSetValue (new_split, val_b);
00566       xaccTransCommitEdit (trans);
00567       xaccAccountCommitEdit (acc);
00568       return new_split;
00569    }
00570 }
00571 
00572 /* ============================================================== */
00573 
00574 /* Accounting-policy callback.  Given an account and an amount, 
00575  * this routine should return a lot.  By implementing this as 
00576  * a callback, we can 'easily' add other accounting policies.
00577  */
00578 gboolean
00579 xaccSplitAssign (Split *split)
00580 {
00581    Account *acc;
00582    gboolean splits_split_up = FALSE;
00583    GNCLot *lot;
00584    GNCPolicy *pcy;
00585 
00586    if (!split) return FALSE;
00587    
00588    /* If this split already belongs to a lot or the account doesn't 
00589     * have lots, we are done. 
00590     */
00591    if (split->lot) return FALSE;
00592    acc = split->acc;
00593    if (!xaccAccountHasTrades (acc))
00594      return FALSE;
00595    if (gnc_numeric_zero_p (split->amount))
00596      return FALSE;
00597 
00598    ENTER ("(split=%p)", split);
00599 
00600    pcy = gnc_account_get_policy(acc);
00601    xaccAccountBeginEdit (acc);
00602 
00603    /* If we are here, this split does not belong to any lot.
00604     * We ask the policy for a lot to assign it to.  This 
00605     * block is written in the form of a while loop, since we
00606     * may have to bust a split across several lots.
00607     */
00608    while (split)
00609    {
00610      PINFO ("have split %p amount=%s", split, 
00611              gnc_num_dbg_to_string (split->amount));
00612      split->gains |= GAINS_STATUS_VDIRTY;
00613      lot = pcy->PolicyGetLot (pcy, split);
00614      if (!lot)
00615      {
00616         lot = gnc_lot_make_default (acc);
00617         PINFO ("start new lot (%s)", gnc_lot_get_title(lot));
00618      }
00619      split = xaccSplitAssignToLot (split, lot);
00620      if (split) splits_split_up = TRUE;
00621    }
00622    xaccAccountCommitEdit (acc);
00623 
00624    LEAVE (" split_up=%d", splits_split_up);
00625    return splits_split_up;
00626 }
00627 
00628 /* ============================================================== */
00629 
00630 Split *
00631 xaccSplitGetCapGainsSplit (const Split *split)
00632 {
00633    KvpValue *val;
00634    GUID *gains_guid;
00635    Split *gains_split;
00636    
00637    if (!split) return NULL;
00638 
00639    val = kvp_frame_get_slot (split->inst.kvp_data, "gains-split");
00640    if (!val) return NULL;
00641    gains_guid = kvp_value_get_guid (val);
00642    if (!gains_guid) return NULL;
00643 
00644    /* Both splits will be in the same collection, so search there. */
00645    gains_split = (Split*) qof_collection_lookup_entity (
00646        qof_instance_get_collection(split), gains_guid);
00647    PINFO ("split=%p has gains-split=%p", split, gains_split);
00648    return gains_split;
00649 }
00650 
00651 /* ============================================================== */
00652 
00653 Split *
00654 xaccSplitGetGainsSourceSplit (const Split *split)
00655 {
00656    KvpValue *val;
00657    GUID *source_guid;
00658    Split *source_split;
00659    
00660    if (!split) return NULL;
00661 
00662    val = kvp_frame_get_slot (split->inst.kvp_data, "gains-source");
00663    if (!val) return NULL;
00664    source_guid = kvp_value_get_guid (val);
00665    if (!source_guid) return NULL;
00666 
00667    /* Both splits will be in the same collection, so search there. */
00668    source_split = (Split*) qof_collection_lookup_entity(
00669        qof_instance_get_collection(split), source_guid);
00670    PINFO ("split=%p has source-split=%p", split, source_split);
00671    return source_split;
00672 }
00673 
00674 /* ============================================================== */
00675 
00676 void
00677 xaccSplitComputeCapGains(Split *split, Account *gain_acc)
00678 {
00679    SplitList *node;
00680    GNCLot *lot;
00681    GNCPolicy *pcy;
00682    gnc_commodity *currency = NULL;
00683    gnc_numeric zero = gnc_numeric_zero();
00684    gnc_numeric value = zero;
00685    gnc_numeric frac;
00686    gnc_numeric opening_amount, opening_value;
00687    gnc_numeric lot_amount, lot_value;
00688    gnc_commodity *opening_currency;
00689 
00690    if (!split) return;
00691    lot = split->lot;
00692    if (!lot) return;
00693    pcy = gnc_account_get_policy(lot->account);
00694    currency = split->parent->common_currency;
00695 
00696    ENTER ("(split=%p gains=%p status=0x%x lot=%s)", split, 
00697        split->gains_split, split->gains,
00698        kvp_frame_get_string (gnc_lot_get_slots (lot), "/title"));
00699 
00700    /* Make sure the status flags and pointers are initialized */
00701    xaccSplitDetermineGainStatus(split);
00702 
00703    /* Not possible to have gains if the transaction currency and 
00704     * account commodity are identical. */
00705    if (gnc_commodity_equal (currency,
00706                             xaccAccountGetCommodity(split->acc)))
00707    {
00708       LEAVE ("Currency transfer, gains not possible, returning.");
00709       return;
00710    }
00711 
00712    if (pcy->PolicyIsOpeningSplit (pcy, lot, split))
00713    {
00714 #if MOVE_THIS_TO_A_DATA_INTEGRITY_SCRUBBER 
00715       /* Check to make sure that this opening split doesn't 
00716        * have a cap-gain transaction associated with it.  
00717        * If it does, that's wrong, and we ruthlessly destroy it.
00718        * XXX Don't do this, it leads to infinite loops.
00719        * We need to scrub out errors like this elsewhere!
00720        */
00721       if (xaccSplitGetCapGainsSplit (split))
00722       {
00723          Split *gains_split = xaccSplitGetCapGainsSplit(split);
00724          Transaction *trans = gains_split->parent;
00725          PERR ("Opening Split must not have cap gains!!\n");
00726          
00727          xaccTransBeginEdit (trans);
00728          xaccTransDestroy (trans);
00729          xaccTransCommitEdit (trans);
00730       }
00731 #endif
00732       LEAVE ("Lot opening split, returning.");
00733       return;
00734    }
00735 
00736    if (safe_strcmp ("stock-split", xaccSplitGetType (split)) == 0)
00737    {
00738       LEAVE ("Stock split split, returning.");
00739       return;
00740    }
00741    
00742    if (GAINS_STATUS_GAINS & split->gains)
00743    {
00744       Split *s;
00745       PINFO ("split is a gains recording split, switch over");
00746       /* If this is the split that records the gains, then work with 
00747        * the split that generates the gains. 
00748        */
00749       /* split = xaccSplitGetCapGainsSplit (split); */
00750       s = split->gains_split;
00751 
00752       /* This should never be NULL, and if it is, and its matching
00753        * parent can't be found, then its a bug, and we should be
00754        * discarding this split.   But ... for now .. return.
00755        * XXX move appropriate actions to a 'scrub' routine'
00756        */
00757       if (!s) 
00758       {
00759          PERR ("Bad gains-split pointer! .. trying to recover.");
00760          split->gains_split = xaccSplitGetCapGainsSplit (split);
00761          s = split->gains_split;
00762          if (!s) return;
00763 #if MOVE_THIS_TO_A_DATA_INTEGRITY_SCRUBBER 
00764          xaccTransDestroy (trans);
00765 #endif
00766       }
00767       split = s;
00768    }
00769 
00770    /* Note: if the value of the 'opening' split(s) has changed,
00771     * then the cap gains are changed. So we need to check not
00772     * only if this split is dirty, but also the lot-opening splits. */
00773    for (node=lot->splits; node; node=node->next)
00774    {
00775       Split *s = node->data;
00776       if (pcy->PolicyIsOpeningSplit(pcy,lot,s))
00777       {
00778          if (GAINS_STATUS_UNKNOWN == s->gains) xaccSplitDetermineGainStatus (s);
00779          if (s->gains & GAINS_STATUS_VDIRTY) 
00780          { 
00781             /* Force a recompute to occur */
00782             split->gains |= GAINS_STATUS_VDIRTY;
00783             break;
00784          }
00785       }
00786    }
00787 
00788    /* If it doesn't look like this split is 'dirty', then there's
00789     * nothing to do. Just return. */
00790    if ((FALSE == (split->gains & GAINS_STATUS_A_VDIRTY))  &&
00791        (split->gains_split) &&
00792        (FALSE == (split->gains_split->gains & GAINS_STATUS_A_VDIRTY))) 
00793    {
00794       LEAVE ("split not dirty, returning");
00795       return;
00796    }
00797 
00798    /* Yow! If amount is zero, there's nothing to do! Amount-zero splits 
00799     * may exist if users attempted to manually record gains. */
00800    if (gnc_numeric_zero_p (split->amount)) return;
00801 
00802    /* If we got to here, then the split or something related is
00803     * 'dirty' and the gains really do need to be recomputed. 
00804     * So start working things. */
00805 
00806    /* Get the amount and value in this lot at the time of this transaction. */
00807    gnc_lot_get_balance_before (lot, split, &lot_amount, &lot_value);
00808 
00809    pcy->PolicyGetLotOpening (pcy, lot, &opening_amount, &opening_value,
00810        &opening_currency);
00811 
00812    /* Check to make sure the lot-opening currency and this split
00813     * use the same currency */
00814    if (FALSE == gnc_commodity_equiv (currency, opening_currency))
00815    {
00816       /* OK, the purchase and the sale were made in different currencies.
00817        * I don't know how to compute cap gains for that.  This is not
00818        * an error. Just punt, silently. 
00819        */
00820       LEAVE ("Can't compute gains, mismatched commodities!");
00821       return;
00822    }
00823 
00824    /* Opening amount should be larger (or equal) to current split,
00825     * and it should be of the opposite sign.
00826     * XXX This should really be a part of a scrub routine that
00827     * cleans up the lot, before we get at it!
00828     */
00829    if (0 > gnc_numeric_compare (gnc_numeric_abs(lot_amount),
00830                                 gnc_numeric_abs(split->amount)))
00831    {
00832       GList *n;
00833       for (n = lot->splits; n; n = n->next) 
00834       {
00835          Split *s = n->data;
00836          PINFO ("split amt=%s", gnc_num_dbg_to_string(s->amount));
00837       }
00838       PERR ("Malformed Lot \"%s\"! (too thin!) " 
00839             "opening amt=%s split amt=%s baln=%s",
00840              gnc_lot_get_title (lot),
00841              gnc_num_dbg_to_string (lot_amount),
00842              gnc_num_dbg_to_string (split->amount),
00843              gnc_num_dbg_to_string (gnc_lot_get_balance(lot)));
00844       return;
00845    }
00846    if ( (gnc_numeric_negative_p(lot_amount) ||
00847          gnc_numeric_positive_p(split->amount)) &&
00848         (gnc_numeric_positive_p(lot_amount) ||
00849          gnc_numeric_negative_p(split->amount)))
00850    {
00851       GList *n;
00852       for (n = lot->splits; n; n = n->next) 
00853       {
00854          Split *s = n->data;
00855          PINFO ("split amt=%s", gnc_num_dbg_to_string(s->amount));
00856       }
00857       PERR ("Malformed Lot \"%s\"! (too fat!) "
00858             "opening amt=%s split amt=%s baln=%s",
00859              gnc_lot_get_title (lot),
00860              gnc_num_dbg_to_string (lot_amount),
00861              gnc_num_dbg_to_string (split->amount),
00862              gnc_num_dbg_to_string (gnc_lot_get_balance(lot)));
00863       return;
00864    }
00865 
00866    /* The cap gains is the difference between the basis prior to the
00867     * current split, and the current split, pro-rated for an equal
00868     * amount of shares. 
00869     * i.e. purchase_price = lot_value / lot_amount 
00870     * cost_basis = purchase_price * current_split_amount
00871     * cap_gain = current_split_value - cost_basis 
00872     */
00873    /* Fraction of the lot that this split represents: */
00874    frac = gnc_numeric_div (split->amount, lot_amount, 
00875                             GNC_DENOM_AUTO, 
00876                             GNC_HOW_DENOM_REDUCE);
00877    /* Basis for this split: */
00878    value = gnc_numeric_mul (frac, lot_value, 
00879                             gnc_numeric_denom(opening_value), 
00880                             GNC_HOW_DENOM_EXACT|GNC_HOW_RND_ROUND);
00881    /* Capital gain for this split: */
00882    value = gnc_numeric_sub (value, split->value,
00883                             GNC_DENOM_AUTO, GNC_HOW_DENOM_FIXED);
00884    PINFO ("Open amt=%s val=%s;  split amt=%s val=%s; gains=%s\n",
00885           gnc_num_dbg_to_string (lot_amount),
00886           gnc_num_dbg_to_string (lot_value),
00887           gnc_num_dbg_to_string (split->amount),
00888           gnc_num_dbg_to_string (split->value),
00889           gnc_num_dbg_to_string (value));
00890    if (gnc_numeric_check (value))
00891    {
00892       PERR ("Numeric overflow during gains calculation\n"
00893             "Acct=%s Txn=%s\n"
00894             "\tOpen amt=%s val=%s\n\tsplit amt=%s val=%s\n\tgains=%s\n",
00895              xaccAccountGetName(split->acc),
00896              xaccTransGetDescription(split->parent),
00897              gnc_num_dbg_to_string (lot_amount),
00898              gnc_num_dbg_to_string (lot_value),
00899              gnc_num_dbg_to_string (split->amount),
00900              gnc_num_dbg_to_string (split->value),
00901              gnc_num_dbg_to_string (value));
00902       return;
00903    }
00904 
00905    /* Are the cap gains zero?  If not, add a balancing transaction.
00906     * As per design doc lots.txt: the transaction has two splits, 
00907     * with equal & opposite values.  The amt of one iz zero (so as
00908     * not to upset the lot balance), the amt of the other is the same 
00909     * as its value (its the realized gain/loss).
00910     */
00911    if (FALSE == gnc_numeric_zero_p (value))
00912    {
00913       Transaction *trans;
00914       Split *lot_split, *gain_split;
00915       Timespec ts;
00916       gboolean new_gain_split;
00917       gnc_numeric negvalue = gnc_numeric_neg (value);
00918 
00919       /* See if there already is an associated gains transaction.
00920        * If there is, adjust its value as appropriate. Else, create 
00921        * a new gains transaction.
00922        */
00923       /* lot_split = xaccSplitGetCapGainsSplit (split);  */
00924       lot_split = split->gains_split;
00925 
00926       if (NULL == lot_split)
00927       {
00928          Account *lot_acc = lot->account;
00929          QofBook *book = qof_instance_get_book(lot_acc);
00930 
00931          new_gain_split = TRUE;
00932          
00933          lot_split = xaccMallocSplit (book);
00934          gain_split = xaccMallocSplit (book);
00935 
00936          /* Check to make sure the gains account currency matches. */
00937          if ((NULL == gain_acc) ||
00938              (FALSE == gnc_commodity_equiv (currency,
00939                                    xaccAccountGetCommodity(gain_acc))))
00940          {
00941             gain_acc = GetOrMakeGainAcct (lot_acc, currency);
00942          }
00943 
00944          xaccAccountBeginEdit (gain_acc);
00945          xaccAccountInsertSplit (gain_acc, gain_split);
00946          xaccAccountCommitEdit (gain_acc);
00947 
00948          xaccAccountBeginEdit (lot_acc);
00949          xaccAccountInsertSplit (lot_acc, lot_split);
00950          xaccAccountCommitEdit (lot_acc);
00951 
00952          trans = xaccMallocTransaction (book);
00953 
00954          xaccTransBeginEdit (trans);
00955          xaccTransSetCurrency (trans, currency);
00956          xaccTransSetDescription (trans, _("Realized Gain/Loss"));
00957          
00958          xaccTransAppendSplit (trans, lot_split);
00959          xaccTransAppendSplit (trans, gain_split);
00960 
00961          xaccSplitSetMemo (lot_split, _("Realized Gain/Loss"));
00962          xaccSplitSetMemo (gain_split, _("Realized Gain/Loss"));
00963 
00964          /* For the new transaction, install KVP markup indicating 
00965           * that this is the gains transaction that corresponds
00966           * to the gains source.
00967           */
00968          kvp_frame_set_guid (split->inst.kvp_data, "gains-split", 
00969                      xaccSplitGetGUID (lot_split));
00970          kvp_frame_set_guid (lot_split->inst.kvp_data, "gains-source", 
00971                      xaccSplitGetGUID (split));
00972 
00973       }
00974       else
00975       {
00976          trans = lot_split->parent;
00977          gain_split = xaccSplitGetOtherSplit (lot_split);
00978          /* If the gain is already recorded corectly do nothing.  This is
00979           * more than just an optimization since this may be called during
00980           * gnc_book_partition_txn and depending on the order in which things
00981           * happen some splits may be in the wrong book at that time. */
00982          if (split->gains_split == lot_split &&
00983              lot_split->gains_split == split &&
00984              gain_split->gains_split == split &&
00985              gnc_numeric_equal (xaccSplitGetValue (lot_split), value) &&
00986              gnc_numeric_zero_p (xaccSplitGetAmount (lot_split)) &&
00987              gnc_numeric_equal (xaccSplitGetValue (gain_split), negvalue) &&
00988              gnc_numeric_equal (xaccSplitGetAmount (gain_split), negvalue))
00989          {
00990             new_gain_split = FALSE;
00991          }
00992          else
00993          {
00994             new_gain_split = TRUE;
00995             xaccTransBeginEdit (trans);
00996 
00997             /* Make sure the existing gains trans has the correct currency,
00998              * just in case someone screwed with it! */
00999             if (FALSE == gnc_commodity_equiv(currency,trans->common_currency))
01000             {
01001                PWARN ("Resetting the transaction currency!");
01002                xaccTransSetCurrency (trans, currency);
01003             }
01004          }
01005       }
01006 
01007       if (new_gain_split)
01008       {
01009          /* Common to both */
01010          ts = xaccTransRetDatePostedTS (split->parent);
01011          xaccTransSetDatePostedTS (trans, &ts);
01012          xaccTransSetDateEnteredSecs (trans, time(0));
01013    
01014          xaccSplitSetAmount (lot_split, zero);
01015          xaccSplitSetValue (lot_split, value);
01016    
01017          xaccSplitSetAmount (gain_split, negvalue);
01018          xaccSplitSetValue (gain_split, negvalue);
01019    
01020          /* Some short-cuts to help avoid the above kvp lookup. */
01021          split->gains = GAINS_STATUS_CLEAN;
01022          split->gains_split = lot_split;
01023          lot_split->gains = GAINS_STATUS_GAINS;
01024          lot_split->gains_split = split;
01025          gain_split->gains = GAINS_STATUS_GAINS;
01026          gain_split->gains_split = split;
01027          
01028          /* Do this last since it may generate an event that will call us
01029             recursively. */
01030          gnc_lot_add_split (lot, lot_split);
01031    
01032          xaccTransCommitEdit (trans);
01033       }
01034    }
01035    LEAVE ("(lot=%s)", gnc_lot_get_title(lot));
01036 }
01037 
01038 /* ============================================================== */
01039 
01040 gnc_numeric 
01041 xaccSplitGetCapGains(Split * split)
01042 {
01043    if (!split) return gnc_numeric_zero();
01044    ENTER("(split=%p)", split);
01045 
01046    if (GAINS_STATUS_UNKNOWN == split->gains) 
01047        xaccSplitDetermineGainStatus(split);
01048    if ((split->gains & GAINS_STATUS_A_VDIRTY) || 
01049        (split->gains_split && 
01050         (split->gains_split->gains & GAINS_STATUS_A_VDIRTY)))
01051    {
01052       xaccSplitComputeCapGains (split, NULL);
01053    }
01054 
01055    /* If this is the source split, get the gains from the one 
01056     * that records the gains.  If this already is the gains split, 
01057     * its a no-op. */
01058    if (!(GAINS_STATUS_GAINS & split->gains))
01059    {
01060       /* split = xaccSplitGetCapGainsSplit (split); */
01061       split = split->gains_split;
01062    }
01063 
01064    LEAVE("(split=%p)", split);
01065    if (!split) return gnc_numeric_zero();
01066 
01067    return split->value;
01068 }
01069 
01070 /* ============================================================== */
01071 
01072 void 
01073 xaccLotComputeCapGains (GNCLot *lot, Account *gain_acc)
01074 {
01075    SplitList *node;
01076    GNCPolicy *pcy;
01077    gboolean is_dirty = FALSE;
01078 
01079    /* Note: if the value of the 'opening' split(s) has changed,
01080     * then the cap gains are changed. To capture this, we need 
01081     * to mark all splits dirty if the opening splits are dirty. */
01082 
01083    ENTER("(lot=%p)", lot);
01084    pcy = gnc_account_get_policy(lot->account);
01085    for (node = lot->splits; node; node = node->next)
01086    {
01087       Split *s = node->data;
01088       if (pcy->PolicyIsOpeningSplit(pcy,lot,s))
01089       {
01090          if (GAINS_STATUS_UNKNOWN == s->gains) 
01091              xaccSplitDetermineGainStatus(s);
01092          if (s->gains & GAINS_STATUS_VDIRTY) 
01093          { 
01094             is_dirty = TRUE; 
01095             s->gains &= ~GAINS_STATUS_VDIRTY;
01096          }
01097       }
01098    }
01099 
01100    if (is_dirty)
01101    {
01102       for (node = lot->splits; node; node = node->next)
01103       {
01104          Split *s = node->data;
01105          s->gains |= GAINS_STATUS_VDIRTY;
01106       }
01107    }
01108 
01109    for (node = lot->splits; node; node = node->next)
01110    {
01111       Split *s = node->data;
01112       xaccSplitComputeCapGains (s, gain_acc);
01113    }
01114    LEAVE("(lot=%p)", lot);
01115 }
01116 
01117 /* =========================== END OF FILE ======================= */

Generated on Thu Aug 28 05:05:22 2008 for GnuCash by  doxygen 1.5.2