mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
go live with first cut of cap gains
git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@9174 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
parent
b22102b982
commit
1700ebf387
@ -22,6 +22,7 @@ libgncmod_engine_la_SOURCES = \
|
||||
Scrub2.c \
|
||||
TransLog.c \
|
||||
Transaction.c \
|
||||
cap-gains.c \
|
||||
gnc-associate-account.c \
|
||||
gnc-commodity.c \
|
||||
gnc-date.c \
|
||||
@ -68,6 +69,7 @@ gncinclude_HEADERS = \
|
||||
Scrub2.h \
|
||||
TransLog.h \
|
||||
Transaction.h \
|
||||
cap-gains.h \
|
||||
engine-helpers.h \
|
||||
glib-helpers.h \
|
||||
gnc-associate-account.h \
|
||||
|
@ -1,4 +1,6 @@
|
||||
/********************************************************************\
|
||||
* Scrub2.c -- Convert Stock Accounts to use Lots *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU General Public License as *
|
||||
* published by the Free Software Foundation; either version 2 of *
|
||||
@ -40,6 +42,7 @@
|
||||
#include "TransactionP.h"
|
||||
#include "Scrub2.h"
|
||||
#include "ScrubP.h"
|
||||
#include "cap-gains.h"
|
||||
#include "gnc-engine.h"
|
||||
#include "gnc-engine-util.h"
|
||||
#include "gnc-lot.h"
|
||||
@ -51,84 +54,10 @@ static short module = MOD_LOT;
|
||||
|
||||
/* ============================================================== */
|
||||
|
||||
gboolean
|
||||
xaccAccountHasTrades (Account *acc)
|
||||
{
|
||||
gnc_commodity *acc_comm;
|
||||
SplitList *node;
|
||||
|
||||
if (!acc) return FALSE;
|
||||
|
||||
acc_comm = acc->commodity;
|
||||
|
||||
for (node=acc->splits; node; node=node->next)
|
||||
{
|
||||
Split *s = node->data;
|
||||
Transaction *t = s->parent;
|
||||
if (acc_comm != t->common_currency) return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* ============================================================== */
|
||||
|
||||
struct early_lot_s
|
||||
{
|
||||
GNCLot *lot;
|
||||
Timespec ts;
|
||||
int (*numeric_pred)(gnc_numeric);
|
||||
};
|
||||
|
||||
static gpointer earliest_helper (GNCLot *lot, gpointer user_data)
|
||||
{
|
||||
struct early_lot_s *els = user_data;
|
||||
Split *s;
|
||||
Transaction *trans;
|
||||
gnc_numeric bal;
|
||||
|
||||
if (gnc_lot_is_closed (lot)) return NULL;
|
||||
|
||||
/* We want a lot whose balance is of the correct sign */
|
||||
bal = gnc_lot_get_balance (lot);
|
||||
if (0 == (els->numeric_pred) (bal)) return NULL;
|
||||
|
||||
s = gnc_lot_get_earliest_split (lot);
|
||||
trans = s->parent;
|
||||
if ((els->ts.tv_sec > trans->date_posted.tv_sec) ||
|
||||
((els->ts.tv_sec == trans->date_posted.tv_sec) &&
|
||||
(els->ts.tv_nsec > trans->date_posted.tv_nsec)))
|
||||
{
|
||||
els->ts = trans->date_posted;
|
||||
els->lot = lot;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GNCLot *
|
||||
xaccAccountFindEarliestOpenLot (Account *acc, gnc_numeric sign)
|
||||
{
|
||||
struct early_lot_s es;
|
||||
|
||||
es.lot = NULL;
|
||||
es.ts.tv_sec = 10000000LL * ((long long) LONG_MAX);
|
||||
es.ts.tv_nsec = 0;
|
||||
|
||||
if (gnc_numeric_positive_p(sign)) es.numeric_pred = gnc_numeric_negative_p;
|
||||
else es.numeric_pred = gnc_numeric_positive_p;
|
||||
|
||||
xaccAccountForEachLot (acc, earliest_helper, &es);
|
||||
return es.lot;
|
||||
}
|
||||
|
||||
/* ============================================================== */
|
||||
|
||||
void
|
||||
xaccAccountScrubLots (Account *acc)
|
||||
{
|
||||
SplitList *node;
|
||||
time_t now = time(0);
|
||||
|
||||
if (!acc) return;
|
||||
|
||||
@ -143,292 +72,16 @@ xaccAccountScrubLots (Account *acc)
|
||||
restart_loop:
|
||||
for (node=acc->splits; node; node=node->next)
|
||||
{
|
||||
gboolean splits_added = FALSE;
|
||||
|
||||
Split * split = node->data;
|
||||
GNCLot *lot = split->lot;
|
||||
|
||||
/* If this split belongs to a lot, its good. */
|
||||
if (lot) continue;
|
||||
|
||||
/* If we are here, this split does not belong to any lot.
|
||||
* Lets put it in the earliest one we can find. This
|
||||
* block is written in the form of a while loop, since we
|
||||
* may have to bust a split across several lots.
|
||||
*/
|
||||
while (split)
|
||||
{
|
||||
PINFO ("have split amount=%s", gnc_numeric_to_string (split->amount));
|
||||
lot = xaccAccountFindEarliestOpenLot (acc, split->amount);
|
||||
if (lot)
|
||||
{
|
||||
/* If the amount is smaller than open balance ... */
|
||||
gnc_numeric baln = gnc_lot_get_balance (lot);
|
||||
int cmp = gnc_numeric_compare (gnc_numeric_abs(split->amount),
|
||||
gnc_numeric_abs(baln));
|
||||
|
||||
PINFO ("found open lot with baln=%s", gnc_numeric_to_string (baln));
|
||||
/* cmp == +1 if amt > baln */
|
||||
if (0 < cmp)
|
||||
{
|
||||
Split * new_split;
|
||||
gnc_numeric amt_a, amt_b, amt_tot;
|
||||
gnc_numeric val_a, val_b, val_tot;
|
||||
gnc_numeric tmp;
|
||||
Transaction *trans;
|
||||
Timespec ts;
|
||||
|
||||
trans = split->parent;
|
||||
xaccTransBeginEdit (trans);
|
||||
|
||||
amt_tot = split->amount;
|
||||
amt_a = gnc_numeric_neg (baln);
|
||||
amt_b = gnc_numeric_sub_fixed (amt_tot, amt_a);
|
||||
PINFO ("++++++++++++++ splitting split into amt = %s + %s",
|
||||
gnc_numeric_to_string(amt_a),
|
||||
gnc_numeric_to_string(amt_b) );
|
||||
|
||||
/* Compute the value so that it holds in the same proportion:
|
||||
* i.e. so that (amt_a / amt_tot) = (val_a / val_tot)
|
||||
*/
|
||||
val_tot = split->value;
|
||||
val_a = gnc_numeric_mul (amt_a, val_tot,
|
||||
GNC_DENOM_AUTO, GNC_DENOM_REDUCE);
|
||||
tmp = gnc_numeric_div (val_a, amt_tot,
|
||||
gnc_numeric_denom(val_tot), GNC_DENOM_EXACT);
|
||||
|
||||
val_a = tmp;
|
||||
val_b = gnc_numeric_sub_fixed (val_tot, val_a);
|
||||
|
||||
PINFO ("split value is = %s = %s + %s",
|
||||
gnc_numeric_to_string(val_tot),
|
||||
gnc_numeric_to_string(val_a),
|
||||
gnc_numeric_to_string(val_b) );
|
||||
|
||||
xaccSplitSetAmount (split, amt_a);
|
||||
xaccSplitSetValue (split, val_a);
|
||||
|
||||
/* Adding this split will have the effect of closing this lot,
|
||||
* because the new balance should be precisely zero. */
|
||||
gnc_lot_add_split (lot, split);
|
||||
|
||||
/* Put the remainder of the balance into a new split,
|
||||
* which is in other respects just a clone of this one */
|
||||
new_split = xaccMallocSplit (acc->book);
|
||||
|
||||
/* Copy most of the split attributes */
|
||||
xaccSplitSetMemo (new_split, xaccSplitGetMemo (split));
|
||||
xaccSplitSetAction (new_split, xaccSplitGetAction (split));
|
||||
xaccSplitSetReconcile (new_split, xaccSplitGetReconcile (split));
|
||||
ts = xaccSplitRetDateReconciledTS (split);
|
||||
xaccSplitSetDateReconciledTS (new_split, &ts);
|
||||
|
||||
/* Copying the KVP tree seems like the right thing to do,
|
||||
* but this is potentially dangerous, depending on how other
|
||||
* users use it. XXX this needs some thinking ... */
|
||||
xaccSplitSetSlots_nc (new_split, kvp_frame_copy(xaccSplitGetSlots (split)));
|
||||
|
||||
/* Make a pair of pointers, to show that these two splits
|
||||
* used to be one ... */
|
||||
gnc_kvp_gemini (xaccSplitGetSlots (split), now,
|
||||
"split_guid", xaccSplitGetGUID (new_split), NULL);
|
||||
gnc_kvp_gemini (xaccSplitGetSlots (new_split), now,
|
||||
"split_guid", xaccSplitGetGUID (split), NULL);
|
||||
|
||||
xaccSplitSetAmount (new_split, amt_b);
|
||||
xaccSplitSetValue (new_split, val_b);
|
||||
|
||||
xaccAccountInsertSplit (acc, new_split);
|
||||
xaccTransAppendSplit (trans, new_split);
|
||||
xaccTransCommitEdit (trans);
|
||||
split = new_split;
|
||||
|
||||
splits_added = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
gnc_lot_add_split (lot, split);
|
||||
split = NULL;
|
||||
PINFO ("added split to lot, new lot baln=%s",
|
||||
gnc_numeric_to_string (gnc_lot_get_balance(lot)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gint64 id;
|
||||
char buff[200];
|
||||
|
||||
/* No lot was found. Start a new lot */
|
||||
PINFO ("start new lot");
|
||||
lot = gnc_lot_new (acc->book);
|
||||
gnc_lot_add_split (lot, split);
|
||||
split = NULL;
|
||||
|
||||
/* Provide a reasonable title for the new lot */
|
||||
id = kvp_frame_get_gint64 (xaccAccountGetSlots (acc), "/lot-mgmt/next-id");
|
||||
snprintf (buff, 200, _("Lot %lld"), id);
|
||||
kvp_frame_set_str (gnc_lot_get_slots (lot), "/title", buff);
|
||||
id ++;
|
||||
kvp_frame_set_gint64 (xaccAccountGetSlots (acc), "/lot-mgmt/next-id", id);
|
||||
}
|
||||
}
|
||||
|
||||
if (splits_added) goto restart_loop;
|
||||
/* If already in lot, then no-op */
|
||||
if (split->lot) continue;
|
||||
if (xaccSplitFIFOAssignToLot (split)) goto restart_loop;
|
||||
}
|
||||
xaccAccountCommitEdit (acc);
|
||||
LEAVE ("acc=%s", acc->accountName);
|
||||
}
|
||||
|
||||
/* ============================================================== */
|
||||
|
||||
static Timespec
|
||||
gnc_lot_get_close_date (GNCLot *lot)
|
||||
{
|
||||
Split *s = gnc_lot_get_latest_split (lot);
|
||||
Transaction *trans = s->parent;
|
||||
return trans->date_posted;
|
||||
}
|
||||
|
||||
/* ============================================================== */
|
||||
/* Similar to GetOrMakeAccount, but different in important ways */
|
||||
|
||||
static Account *
|
||||
GetOrMakeLotOrphanAccount (AccountGroup *root, gnc_commodity * currency)
|
||||
{
|
||||
char * accname;
|
||||
Account * acc;
|
||||
|
||||
g_return_val_if_fail (root, NULL);
|
||||
|
||||
/* build the account name */
|
||||
if (!currency)
|
||||
{
|
||||
PERR ("No currency specified!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
accname = g_strconcat (_("Orphaned Gains"), "-",
|
||||
gnc_commodity_get_mnemonic (currency), NULL);
|
||||
|
||||
/* See if we've got one of these going already ... */
|
||||
acc = xaccGetAccountFromName (root, accname);
|
||||
|
||||
if (acc == NULL)
|
||||
{
|
||||
/* Guess not. We'll have to build one. */
|
||||
acc = xaccMallocAccount (root->book);
|
||||
xaccAccountBeginEdit (acc);
|
||||
xaccAccountSetName (acc, accname);
|
||||
xaccAccountSetCommodity (acc, currency);
|
||||
xaccAccountSetType (acc, INCOME);
|
||||
xaccAccountSetDescription (acc, _("Realized Gain/Loss"));
|
||||
xaccAccountSetNotes (acc,
|
||||
_("Realized Gains or Losses from\n"
|
||||
"Commodity or Trading Accounts\n"
|
||||
"that haven't been recorded elsewhere.\n"));
|
||||
|
||||
/* Hang the account off the root. */
|
||||
xaccGroupInsertAccount (root, acc);
|
||||
xaccAccountCommitEdit (acc);
|
||||
}
|
||||
|
||||
g_free (accname);
|
||||
|
||||
return acc;
|
||||
}
|
||||
|
||||
/* ============================================================== */
|
||||
|
||||
void
|
||||
xaccAccountSetDefaultGainAccount (Account *acc, Account *gain_acct)
|
||||
{
|
||||
KvpFrame *cwd;
|
||||
KvpValue *vvv;
|
||||
const char * cur_name;
|
||||
|
||||
if (!acc || !gain_acct) return;
|
||||
|
||||
cwd = xaccAccountGetSlots (acc);
|
||||
cwd = kvp_frame_get_frame_slash (cwd, "/lot-mgmt/gains-act/");
|
||||
|
||||
/* Accounts are indexed by thier unique currency name */
|
||||
cur_name = gnc_commodity_get_unique_name (acc->commodity);
|
||||
|
||||
xaccAccountBeginEdit (acc);
|
||||
vvv = kvp_value_new_guid (xaccAccountGetGUID (gain_acct));
|
||||
kvp_frame_set_slot_nc (cwd, cur_name, vvv);
|
||||
xaccAccountSetSlots_nc (acc, acc->kvp_data);
|
||||
xaccAccountCommitEdit (acc);
|
||||
}
|
||||
|
||||
/* ============================================================== */
|
||||
|
||||
Account *
|
||||
xaccAccountGetDefaultGainAccount (Account *acc, gnc_commodity * currency)
|
||||
{
|
||||
Account *gain_acct = NULL;
|
||||
KvpFrame *cwd;
|
||||
KvpValue *vvv;
|
||||
GUID * gain_acct_guid;
|
||||
const char * cur_name;
|
||||
|
||||
if (!acc || !currency) return NULL;
|
||||
|
||||
cwd = xaccAccountGetSlots (acc);
|
||||
cwd = kvp_frame_get_frame_slash (cwd, "/lot-mgmt/gains-act/");
|
||||
|
||||
/* Accounts are indexed by thier unique currency name */
|
||||
cur_name = gnc_commodity_get_unique_name (currency);
|
||||
vvv = kvp_frame_get_slot (cwd, cur_name);
|
||||
gain_acct_guid = kvp_value_get_guid (vvv);
|
||||
|
||||
gain_acct = xaccAccountLookup (gain_acct_guid, acc->book);
|
||||
return gain_acct;
|
||||
}
|
||||
|
||||
/* ============================================================== */
|
||||
/* Functionally identical to the following:
|
||||
* if (!xaccAccountGetDefaultGainAccount()) xaccAccountSetDefaultGainAccount ();
|
||||
* except that it saves a few cycles.
|
||||
*/
|
||||
|
||||
static Account *
|
||||
GetOrMakeGainAcct (Account *acc, gnc_commodity * currency)
|
||||
{
|
||||
Account *gain_acct = NULL;
|
||||
KvpFrame *cwd;
|
||||
KvpValue *vvv;
|
||||
GUID * gain_acct_guid;
|
||||
const char * cur_name;
|
||||
|
||||
cwd = xaccAccountGetSlots (acc);
|
||||
cwd = kvp_frame_get_frame_slash (cwd, "/lot-mgmt/gains-act/");
|
||||
|
||||
/* Accounts are indexed by thier unique currency name */
|
||||
cur_name = gnc_commodity_get_unique_name (currency);
|
||||
vvv = kvp_frame_get_slot (cwd, cur_name);
|
||||
gain_acct_guid = kvp_value_get_guid (vvv);
|
||||
|
||||
gain_acct = xaccAccountLookup (gain_acct_guid, acc->book);
|
||||
|
||||
/* If there is no default place to put gains/losses
|
||||
* for this account, then create such a place */
|
||||
if (NULL == gain_acct)
|
||||
{
|
||||
AccountGroup *root;
|
||||
|
||||
xaccAccountBeginEdit (acc);
|
||||
root = xaccAccountGetRoot(acc);
|
||||
gain_acct = GetOrMakeLotOrphanAccount (root, currency);
|
||||
|
||||
vvv = kvp_value_new_guid (xaccAccountGetGUID (gain_acct));
|
||||
kvp_frame_set_slot_nc (cwd, cur_name, vvv);
|
||||
xaccAccountSetSlots_nc (acc, acc->kvp_data);
|
||||
xaccAccountCommitEdit (acc);
|
||||
|
||||
}
|
||||
return gain_acct;
|
||||
}
|
||||
|
||||
/* ============================================================== */
|
||||
|
||||
@ -468,6 +121,12 @@ xaccLotScrubDoubleBalance (GNCLot *lot)
|
||||
|
||||
ENTER ("lot=%s", kvp_frame_get_string (gnc_lot_get_slots (lot), "/title"));
|
||||
|
||||
for (snode = lot->splits; snode; snode=snode->next)
|
||||
{
|
||||
Split *s = snode->data;
|
||||
xaccSplitComputeCapGains (s, NULL);
|
||||
}
|
||||
|
||||
for (snode = lot->splits; snode; snode=snode->next)
|
||||
{
|
||||
Split *s = snode->data;
|
||||
@ -480,11 +139,12 @@ xaccLotScrubDoubleBalance (GNCLot *lot)
|
||||
}
|
||||
if (FALSE == gnc_commodity_equiv (currency, trans->common_currency))
|
||||
{
|
||||
/* Unhandled error condition. We should do something
|
||||
* graceful here. Don't know what. FIXME XXX */
|
||||
PERR ("currencies in lot are not equivalent\n"
|
||||
/* This lot has mixed currencies. Can't double-balance.
|
||||
* Silently punt */
|
||||
PWARN ("Lot with multiple currencies:\n"
|
||||
"\ttrans=%s curr=%s\n", xaccTransGetDescription(trans),
|
||||
gnc_commodity_get_fullname(trans->common_currency));
|
||||
break;
|
||||
}
|
||||
|
||||
/* Now, total up the values */
|
||||
@ -494,60 +154,14 @@ xaccLotScrubDoubleBalance (GNCLot *lot)
|
||||
gnc_numeric_to_string (xaccSplitGetValue(s)),
|
||||
gnc_numeric_to_string (value));
|
||||
|
||||
if (FALSE == gnc_numeric_equal (value, zero))
|
||||
{
|
||||
/* Unhandled error condition. Not sure what to do here,
|
||||
* Since the ComputeCapGains should have gotten it right. */
|
||||
PERR ("Closed lot fails to double-balance !!\n");
|
||||
}
|
||||
}
|
||||
|
||||
PINFO ("Lot value=%s", gnc_numeric_to_string (value));
|
||||
|
||||
/* Is the value of the lot zero? If not, add a balancing transaction.
|
||||
* As per design doc lots.txt: the transaction has two splits,
|
||||
* with equal & opposite values. The amt of one iz zero (so as
|
||||
* not to upset the lot balance), the amt of the other is the same
|
||||
* as its value (its the realized gain/loss).
|
||||
*/
|
||||
if (FALSE == gnc_numeric_equal (value, zero))
|
||||
{
|
||||
Transaction *trans;
|
||||
Account *lot_acc = lot->account;
|
||||
QofBook *book = lot_acc->book;
|
||||
Account *gain_acc;
|
||||
Split *lot_split, *gain_split;
|
||||
Timespec ts;
|
||||
|
||||
lot_split = xaccMallocSplit (book);
|
||||
gain_split = xaccMallocSplit (book);
|
||||
|
||||
gain_acc = GetOrMakeGainAcct (lot_acc, currency);
|
||||
xaccAccountBeginEdit (gain_acc);
|
||||
xaccAccountInsertSplit (gain_acc, gain_split);
|
||||
xaccAccountCommitEdit (gain_acc);
|
||||
|
||||
xaccAccountBeginEdit (lot_acc);
|
||||
xaccAccountInsertSplit (lot_acc, lot_split);
|
||||
xaccAccountCommitEdit (lot_acc);
|
||||
|
||||
trans = xaccMallocTransaction (book);
|
||||
|
||||
xaccTransBeginEdit (trans);
|
||||
xaccTransSetCurrency (trans, currency);
|
||||
xaccTransSetDescription (trans, _("Realized Gain/Loss"));
|
||||
ts = gnc_lot_get_close_date (lot);
|
||||
xaccTransSetDatePostedTS (trans, &ts);
|
||||
xaccTransSetDateEnteredSecs (trans, time(0));
|
||||
|
||||
xaccTransAppendSplit (trans, lot_split);
|
||||
xaccTransAppendSplit (trans, gain_split);
|
||||
|
||||
xaccSplitSetMemo (lot_split, _("Realized Gain/Loss"));
|
||||
xaccSplitSetAmount (lot_split, zero);
|
||||
xaccSplitSetValue (lot_split, gnc_numeric_neg (value));
|
||||
gnc_lot_add_split (lot, lot_split);
|
||||
|
||||
xaccSplitSetMemo (gain_split, _("Realized Gain/Loss"));
|
||||
xaccSplitSetAmount (gain_split, value);
|
||||
xaccSplitSetValue (gain_split, value);
|
||||
xaccTransCommitEdit (trans);
|
||||
|
||||
}
|
||||
LEAVE ("lot=%s", kvp_frame_get_string (gnc_lot_get_slots (lot), "/title"));
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,6 @@
|
||||
/********************************************************************\
|
||||
* Scrub2.h -- Convert Stock Accounts to use Lots *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU General Public License as *
|
||||
* published by the Free Software Foundation; either version 2 of *
|
||||
@ -36,47 +38,6 @@
|
||||
|
||||
#include "gnc-engine.h"
|
||||
|
||||
/** 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.
|
||||
*/
|
||||
gboolean xaccAccountHasTrades (Account *);
|
||||
|
||||
/** 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.
|
||||
*/
|
||||
GNCLot * xaccAccountFindEarliestOpenLot (Account *acc, gnc_numeric sign);
|
||||
|
||||
/** 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.
|
||||
*/
|
||||
Account * xaccAccountGetDefaultGainAccount (Account *acc, gnc_commodity * currency);
|
||||
|
||||
/** 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.
|
||||
*/
|
||||
void xaccAccountSetDefaultGainAccount (Account *acc, Account *gains_acct);
|
||||
|
||||
/** The xaccAccountScrubLots() routine will walk over all of
|
||||
* the splits in an account, and make sure that each belongs
|
||||
* to a lot. Any splits that are not in a lot will be used
|
||||
@ -87,7 +48,6 @@ void xaccAccountSetDefaultGainAccount (Account *acc, Account *gains_acct);
|
||||
*/
|
||||
void xaccAccountScrubLots (Account *acc);
|
||||
|
||||
|
||||
/** The xaccAccountScrubDoubleBalance() routine examines all
|
||||
* of the closed lots in an account, and verifies that the
|
||||
* lots are 'double balanced'. By 'double balance', we mean
|
||||
|
@ -61,6 +61,7 @@ ToDo List:
|
||||
#include "GroupP.h"
|
||||
#include "Transaction.h"
|
||||
#include "TransactionP.h"
|
||||
#include "cap-gains.h"
|
||||
#include "gnc-engine.h"
|
||||
#include "gnc-engine-util.h"
|
||||
#include "gnc-lot.h"
|
||||
@ -72,23 +73,230 @@ static short module = MOD_LOT;
|
||||
|
||||
|
||||
/* ============================================================== */
|
||||
/** The`xaccSplitFIFOAssignToLot() routine will take the indicated
|
||||
* split and assign it to the earliest open lot that it can find.
|
||||
* 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 entrety, 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.
|
||||
*
|
||||
* Because this routine always uses the earliest open lot, it
|
||||
* implments a "FIFO" First-In First-Out accounting policy.
|
||||
*
|
||||
|
||||
gboolean
|
||||
xaccAccountHasTrades (Account *acc)
|
||||
{
|
||||
gnc_commodity *acc_comm;
|
||||
SplitList *node;
|
||||
|
||||
if (!acc) return FALSE;
|
||||
|
||||
acc_comm = acc->commodity;
|
||||
|
||||
for (node=acc->splits; node; node=node->next)
|
||||
{
|
||||
Split *s = node->data;
|
||||
Transaction *t = s->parent;
|
||||
if (acc_comm != t->common_currency) return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* ============================================================== */
|
||||
|
||||
struct early_lot_s
|
||||
{
|
||||
GNCLot *lot;
|
||||
Timespec ts;
|
||||
int (*numeric_pred)(gnc_numeric);
|
||||
};
|
||||
|
||||
static gpointer earliest_helper (GNCLot *lot, gpointer user_data)
|
||||
{
|
||||
struct early_lot_s *els = user_data;
|
||||
Split *s;
|
||||
Transaction *trans;
|
||||
gnc_numeric bal;
|
||||
|
||||
if (gnc_lot_is_closed (lot)) return NULL;
|
||||
|
||||
/* We want a lot whose balance is of the correct sign */
|
||||
bal = gnc_lot_get_balance (lot);
|
||||
if (0 == (els->numeric_pred) (bal)) return NULL;
|
||||
|
||||
s = gnc_lot_get_earliest_split (lot);
|
||||
trans = s->parent;
|
||||
if ((els->ts.tv_sec > trans->date_posted.tv_sec) ||
|
||||
((els->ts.tv_sec == trans->date_posted.tv_sec) &&
|
||||
(els->ts.tv_nsec > trans->date_posted.tv_nsec)))
|
||||
{
|
||||
els->ts = trans->date_posted;
|
||||
els->lot = lot;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GNCLot *
|
||||
xaccAccountFindEarliestOpenLot (Account *acc, gnc_numeric sign)
|
||||
{
|
||||
struct early_lot_s es;
|
||||
|
||||
es.lot = NULL;
|
||||
es.ts.tv_sec = 10000000LL * ((long long) LONG_MAX);
|
||||
es.ts.tv_nsec = 0;
|
||||
|
||||
if (gnc_numeric_positive_p(sign)) es.numeric_pred = gnc_numeric_negative_p;
|
||||
else es.numeric_pred = gnc_numeric_positive_p;
|
||||
|
||||
xaccAccountForEachLot (acc, earliest_helper, &es);
|
||||
return es.lot;
|
||||
}
|
||||
|
||||
/* ============================================================== */
|
||||
|
||||
static Timespec
|
||||
gnc_lot_get_close_date (GNCLot *lot)
|
||||
{
|
||||
Split *s = gnc_lot_get_latest_split (lot);
|
||||
Transaction *trans = s->parent;
|
||||
return trans->date_posted;
|
||||
}
|
||||
|
||||
/* ============================================================== */
|
||||
/* Similar to GetOrMakeAccount, but different in important ways */
|
||||
|
||||
static Account *
|
||||
GetOrMakeLotOrphanAccount (AccountGroup *root, gnc_commodity * currency)
|
||||
{
|
||||
char * accname;
|
||||
Account * acc;
|
||||
|
||||
g_return_val_if_fail (root, NULL);
|
||||
|
||||
/* build the account name */
|
||||
if (!currency)
|
||||
{
|
||||
PERR ("No currency specified!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
accname = g_strconcat (_("Orphaned Gains"), "-",
|
||||
gnc_commodity_get_mnemonic (currency), NULL);
|
||||
|
||||
/* See if we've got one of these going already ... */
|
||||
acc = xaccGetAccountFromName (root, accname);
|
||||
|
||||
if (acc == NULL)
|
||||
{
|
||||
/* Guess not. We'll have to build one. */
|
||||
acc = xaccMallocAccount (root->book);
|
||||
xaccAccountBeginEdit (acc);
|
||||
xaccAccountSetName (acc, accname);
|
||||
xaccAccountSetCommodity (acc, currency);
|
||||
xaccAccountSetType (acc, INCOME);
|
||||
xaccAccountSetDescription (acc, _("Realized Gain/Loss"));
|
||||
xaccAccountSetNotes (acc,
|
||||
_("Realized Gains or Losses from\n"
|
||||
"Commodity or Trading Accounts\n"
|
||||
"that haven't been recorded elsewhere.\n"));
|
||||
|
||||
/* Hang the account off the root. */
|
||||
xaccGroupInsertAccount (root, acc);
|
||||
xaccAccountCommitEdit (acc);
|
||||
}
|
||||
|
||||
g_free (accname);
|
||||
|
||||
return acc;
|
||||
}
|
||||
|
||||
/* ============================================================== */
|
||||
|
||||
void
|
||||
xaccAccountSetDefaultGainAccount (Account *acc, Account *gain_acct)
|
||||
{
|
||||
KvpFrame *cwd;
|
||||
KvpValue *vvv;
|
||||
const char * cur_name;
|
||||
|
||||
if (!acc || !gain_acct) return;
|
||||
|
||||
cwd = xaccAccountGetSlots (acc);
|
||||
cwd = kvp_frame_get_frame_slash (cwd, "/lot-mgmt/gains-act/");
|
||||
|
||||
/* Accounts are indexed by thier unique currency name */
|
||||
cur_name = gnc_commodity_get_unique_name (acc->commodity);
|
||||
|
||||
xaccAccountBeginEdit (acc);
|
||||
vvv = kvp_value_new_guid (xaccAccountGetGUID (gain_acct));
|
||||
kvp_frame_set_slot_nc (cwd, cur_name, vvv);
|
||||
xaccAccountSetSlots_nc (acc, acc->kvp_data);
|
||||
xaccAccountCommitEdit (acc);
|
||||
}
|
||||
|
||||
/* ============================================================== */
|
||||
|
||||
Account *
|
||||
xaccAccountGetDefaultGainAccount (Account *acc, gnc_commodity * currency)
|
||||
{
|
||||
Account *gain_acct = NULL;
|
||||
KvpFrame *cwd;
|
||||
KvpValue *vvv;
|
||||
GUID * gain_acct_guid;
|
||||
const char * cur_name;
|
||||
|
||||
if (!acc || !currency) return NULL;
|
||||
|
||||
cwd = xaccAccountGetSlots (acc);
|
||||
cwd = kvp_frame_get_frame_slash (cwd, "/lot-mgmt/gains-act/");
|
||||
|
||||
/* Accounts are indexed by thier unique currency name */
|
||||
cur_name = gnc_commodity_get_unique_name (currency);
|
||||
vvv = kvp_frame_get_slot (cwd, cur_name);
|
||||
gain_acct_guid = kvp_value_get_guid (vvv);
|
||||
|
||||
gain_acct = xaccAccountLookup (gain_acct_guid, acc->book);
|
||||
return gain_acct;
|
||||
}
|
||||
|
||||
/* ============================================================== */
|
||||
/* Functionally identical to the following:
|
||||
* if (!xaccAccountGetDefaultGainAccount()) xaccAccountSetDefaultGainAccount ();
|
||||
* except that it saves a few cycles.
|
||||
*/
|
||||
|
||||
static Account *
|
||||
GetOrMakeGainAcct (Account *acc, gnc_commodity * currency)
|
||||
{
|
||||
Account *gain_acct = NULL;
|
||||
KvpFrame *cwd;
|
||||
KvpValue *vvv;
|
||||
GUID * gain_acct_guid;
|
||||
const char * cur_name;
|
||||
|
||||
cwd = xaccAccountGetSlots (acc);
|
||||
cwd = kvp_frame_get_frame_slash (cwd, "/lot-mgmt/gains-act/");
|
||||
|
||||
/* Accounts are indexed by thier unique currency name */
|
||||
cur_name = gnc_commodity_get_unique_name (currency);
|
||||
vvv = kvp_frame_get_slot (cwd, cur_name);
|
||||
gain_acct_guid = kvp_value_get_guid (vvv);
|
||||
|
||||
gain_acct = xaccAccountLookup (gain_acct_guid, acc->book);
|
||||
|
||||
/* If there is no default place to put gains/losses
|
||||
* for this account, then create such a place */
|
||||
if (NULL == gain_acct)
|
||||
{
|
||||
AccountGroup *root;
|
||||
|
||||
xaccAccountBeginEdit (acc);
|
||||
root = xaccAccountGetRoot(acc);
|
||||
gain_acct = GetOrMakeLotOrphanAccount (root, currency);
|
||||
|
||||
vvv = kvp_value_new_guid (xaccAccountGetGUID (gain_acct));
|
||||
kvp_frame_set_slot_nc (cwd, cur_name, vvv);
|
||||
xaccAccountSetSlots_nc (acc, acc->kvp_data);
|
||||
xaccAccountCommitEdit (acc);
|
||||
|
||||
}
|
||||
return gain_acct;
|
||||
}
|
||||
|
||||
/* ============================================================== */
|
||||
/* Accounting-policy callback. Given an account and an amount,
|
||||
* this routine should return a lot.
|
||||
*/
|
||||
@ -109,7 +317,7 @@ xaccSplitAssignToLot (Split *split,
|
||||
|
||||
/* If this split already belongs to a lot, we are done. */
|
||||
if (split->lot) return FALSE;
|
||||
acc = split->account;
|
||||
acc = split->acc;
|
||||
xaccAccountBeginEdit (acc);
|
||||
|
||||
/* If we are here, this split does not belong to any lot.
|
||||
@ -199,11 +407,21 @@ xaccSplitAssignToLot (Split *split,
|
||||
}
|
||||
else
|
||||
{
|
||||
gint64 id;
|
||||
char buff[200];
|
||||
|
||||
/* No lot was found. Start a new lot */
|
||||
PINFO ("start new lot");
|
||||
lot = gnc_lot_new (acc->book);
|
||||
gnc_lot_add_split (lot, split);
|
||||
split = NULL;
|
||||
|
||||
/* Provide a reasonable title for the new lot */
|
||||
id = kvp_frame_get_gint64 (xaccAccountGetSlots (acc), "/lot-mgmt/next-id");
|
||||
snprintf (buff, 200, _("Lot %lld"), id);
|
||||
kvp_frame_set_str (gnc_lot_get_slots (lot), "/title", buff);
|
||||
id ++;
|
||||
kvp_frame_set_gint64 (xaccAccountGetSlots (acc), "/lot-mgmt/next-id", id);
|
||||
}
|
||||
}
|
||||
xaccAccountCommitEdit (acc);
|
||||
@ -247,50 +465,50 @@ void
|
||||
xaccSplitComputeCapGains(Split *split, Account *gain_acc)
|
||||
{
|
||||
Split *opening_split;
|
||||
GNCLot *lot;
|
||||
GNCLot *lot;
|
||||
gnc_commodity *currency = NULL;
|
||||
gnc_numeric zero = gnc_numeric_zero();
|
||||
gnc_numeric value = zero;
|
||||
|
||||
if (!split) return;
|
||||
lot = split->lot;
|
||||
if (!split) return;
|
||||
lot = split->lot;
|
||||
if (!lot) return;
|
||||
currency = split->parent->common_currency;
|
||||
|
||||
ENTER ("lot=%s", kvp_frame_get_string (gnc_lot_get_slots (lot), "/title"));
|
||||
|
||||
opening_split = gnc_lot_get_earliest_split(lot);
|
||||
if (split == opening_split)
|
||||
{
|
||||
/* XXX we should check to make sure this split
|
||||
* doesn't have a cap-gain xaction associated with it.
|
||||
* If it does, itshould be trashed.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check to make sure the opening split and this split
|
||||
* use the same currency */
|
||||
if (FALSE == gnc_commodity_equiv (currency,
|
||||
opening_split->parent->common_currency))
|
||||
opening_split = gnc_lot_get_earliest_split(lot);
|
||||
if (split == opening_split)
|
||||
{
|
||||
/* OK, the purchase and the sale were made in different currencies.
|
||||
* I don't know how to compute cap gains for that. This is not
|
||||
* an error. Just punt, silently.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
/* XXX we should check to make sure this split
|
||||
* doesn't have a cap-gain xaction associated with it.
|
||||
* If it does, itshould be trashed.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check to make sure the opening split and this split
|
||||
* use the same currency */
|
||||
if (FALSE == gnc_commodity_equiv (currency,
|
||||
opening_split->parent->common_currency))
|
||||
{
|
||||
/* OK, the purchase and the sale were made in different currencies.
|
||||
* I don't know how to compute cap gains for that. This is not
|
||||
* an error. Just punt, silently.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
/* The cap gains is the difference between the value of the
|
||||
* opening split, and the current split. */
|
||||
value = xaccSplitGetValue (opening_split);
|
||||
/* The cap gains is the difference between the value of the
|
||||
* opening split, and the current split. */
|
||||
value = xaccSplitGetValue (opening_split);
|
||||
value = gnc_numeric_add (value, xaccSplitGetValue (split),
|
||||
GNC_DENOM_AUTO, GNC_DENOM_LCD);
|
||||
PINFO ("Split value=%s Cap Gains=%s",
|
||||
gnc_numeric_to_string (xaccSplitGetValue(split)),
|
||||
gnc_numeric_to_string (value));
|
||||
|
||||
/* XXX pro-rate based on amounts! */
|
||||
/* XXX pro-rate based on amounts! */
|
||||
|
||||
/* Are the cap gains zero? If not, add a balancing transaction.
|
||||
* As per design doc lots.txt: the transaction has two splits,
|
||||
@ -309,10 +527,10 @@ xaccSplitComputeCapGains(Split *split, Account *gain_acc)
|
||||
lot_split = xaccMallocSplit (book);
|
||||
gain_split = xaccMallocSplit (book);
|
||||
|
||||
if (NULL == gain_acc)
|
||||
{
|
||||
gain_acc = GetOrMakeGainAcct (lot_acc, currency);
|
||||
}
|
||||
if (NULL == gain_acc)
|
||||
{
|
||||
gain_acc = GetOrMakeGainAcct (lot_acc, currency);
|
||||
}
|
||||
xaccAccountBeginEdit (gain_acc);
|
||||
xaccAccountInsertSplit (gain_acc, gain_split);
|
||||
xaccAccountCommitEdit (gain_acc);
|
||||
@ -321,14 +539,14 @@ xaccSplitComputeCapGains(Split *split, Account *gain_acc)
|
||||
xaccAccountInsertSplit (lot_acc, lot_split);
|
||||
xaccAccountCommitEdit (lot_acc);
|
||||
|
||||
/* XXX See if there already is an associated
|
||||
* gains transaction; if there is, adjust its value
|
||||
* as appropriate. Else, create a new gains xaction.
|
||||
*
|
||||
* XXX for new xacton, install KVP markup indicating
|
||||
* that this is the gains trnasaction matching the
|
||||
* orig transaction.
|
||||
*/
|
||||
/* XXX See if there already is an associated
|
||||
* gains transaction; if there is, adjust its value
|
||||
* as appropriate. Else, create a new gains xaction.
|
||||
*
|
||||
* XXX for new xacton, install KVP markup indicating
|
||||
* that this is the gains trnasaction matching the
|
||||
* orig transaction.
|
||||
*/
|
||||
trans = xaccMallocTransaction (book);
|
||||
|
||||
xaccTransBeginEdit (trans);
|
||||
@ -349,7 +567,7 @@ xaccSplitComputeCapGains(Split *split, Account *gain_acc)
|
||||
xaccSplitSetMemo (gain_split, _("Realized Gain/Loss"));
|
||||
xaccSplitSetAmount (gain_split, value);
|
||||
xaccSplitSetValue (gain_split, value);
|
||||
|
||||
|
||||
xaccTransCommitEdit (trans);
|
||||
|
||||
}
|
||||
|
130
src/engine/cap-gains.h
Normal file
130
src/engine/cap-gains.h
Normal file
@ -0,0 +1,130 @@
|
||||
/********************************************************************\
|
||||
* cap-gains.h -- Automatically Compute Capital Gains/Losses *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU General Public License as *
|
||||
* published by the Free Software Foundation; either version 2 of *
|
||||
* the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License*
|
||||
* along with this program; if not, contact: *
|
||||
* *
|
||||
* Free Software Foundation Voice: +1-617-542-5942 *
|
||||
* 59 Temple Place - Suite 330 Fax: +1-617-542-2652 *
|
||||
* Boston, MA 02111-1307, USA gnu@gnu.org *
|
||||
\********************************************************************/
|
||||
|
||||
/** @addtogroup Engine
|
||||
* @{ */
|
||||
/** @file cap-gains.h
|
||||
* @breif Utilities to Automatically Compute Capital Gains/Losses.
|
||||
* @author Created by Linas Vepstas August 2003
|
||||
* @author Copyright (c) 2003 Linas Vepstas <linas@linas.org>
|
||||
*
|
||||
* 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 'alpha': many important
|
||||
* routines are missing, many existing routines are not called
|
||||
* from inside the engine as needed, and routines may be buggy.
|
||||
*
|
||||
* This code does not currently handle tax distinctions, e.g
|
||||
* the different tax treatment that short-term and long-term
|
||||
* cap gains have.
|
||||
*/
|
||||
|
||||
#ifndef XACC_CAP_GAINS_H
|
||||
#define XACC_CAP_GAINS_H
|
||||
|
||||
#include "gnc-engine.h"
|
||||
|
||||
/** 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.
|
||||
*/
|
||||
gboolean xaccAccountHasTrades (Account *);
|
||||
|
||||
/** 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.
|
||||
*/
|
||||
GNCLot * xaccAccountFindEarliestOpenLot (Account *acc, gnc_numeric sign);
|
||||
|
||||
/** 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.
|
||||
*/
|
||||
Account * xaccAccountGetDefaultGainAccount (Account *acc, gnc_commodity * currency);
|
||||
|
||||
/** 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.
|
||||
*/
|
||||
void xaccAccountSetDefaultGainAccount (Account *acc, Account *gains_acct);
|
||||
|
||||
|
||||
/** The`xaccSplitFIFOAssignToLot() routine will take the indicated
|
||||
* split and assign it to the earliest open lot that it can find.
|
||||
* 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 entrety, 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.
|
||||
*
|
||||
* Because this routine always uses the earliest open lot, it
|
||||
* implments a "FIFO" First-In First-Out accounting policy.
|
||||
*
|
||||
*/
|
||||
|
||||
gboolean xaccSplitFIFOAssignToLot (Split *split);
|
||||
|
||||
/** 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.
|
||||
*/
|
||||
|
||||
void xaccSplitComputeCapGains(Split *split, Account *gain_acc);
|
||||
|
||||
#endif /* XACC_CAP_GAINS_H */
|
||||
/** @} */
|
||||
|
||||
/* =========================== END OF FILE ======================= */
|
Loading…
Reference in New Issue
Block a user