mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
merge branch cap-gains6:
new code that recalculates lots (incl. cap gains) to be used if the value of any split in the lot changes. git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@9341 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
parent
3573ffc694
commit
572d2faa56
@ -41,6 +41,7 @@ libgncmod_engine_la_SOURCES = \
|
||||
kvp-util.c \
|
||||
md5.c \
|
||||
messages.c \
|
||||
policy.c \
|
||||
qofbackend.c \
|
||||
qofbook.c \
|
||||
qofid.c \
|
||||
@ -93,6 +94,7 @@ gncinclude_HEADERS = \
|
||||
kvp-scm.h \
|
||||
kvp-util.h \
|
||||
messages.h \
|
||||
policy.h \
|
||||
qof.h \
|
||||
qofbackend.h \
|
||||
qofbook.h \
|
||||
@ -121,6 +123,7 @@ noinst_HEADERS = \
|
||||
md5.h \
|
||||
gw-engine.h \
|
||||
gw-kvp.h \
|
||||
policy-p.h \
|
||||
qofbackend-p.h \
|
||||
qofbook-p.h \
|
||||
qofid-p.h \
|
||||
|
@ -50,13 +50,14 @@
|
||||
#include "gnc-trace.h"
|
||||
#include "kvp-util-p.h"
|
||||
#include "messages.h"
|
||||
#include "policy-p.h"
|
||||
|
||||
static short module = MOD_LOT;
|
||||
|
||||
/* ============================================================== */
|
||||
|
||||
void
|
||||
xaccAccountScrubLots (Account *acc)
|
||||
xaccAccountAssignLots (Account *acc)
|
||||
{
|
||||
SplitList *node;
|
||||
|
||||
@ -67,8 +68,7 @@ xaccAccountScrubLots (Account *acc)
|
||||
|
||||
/* Loop over all splits, and make sure that every split
|
||||
* belongs to some lot. If a split does not belong to
|
||||
* any lots, its is placed into the earliest possible
|
||||
* lot (thus enforcing FIFO accounting rules).
|
||||
* any lots, poke it into one.
|
||||
*/
|
||||
restart_loop:
|
||||
for (node=acc->splits; node; node=node->next)
|
||||
@ -77,13 +77,61 @@ restart_loop:
|
||||
|
||||
/* If already in lot, then no-op */
|
||||
if (split->lot) continue;
|
||||
if (xaccSplitFIFOAssignToLot (split)) goto restart_loop;
|
||||
if (xaccSplitAssign (split)) goto restart_loop;
|
||||
}
|
||||
xaccAccountCommitEdit (acc);
|
||||
LEAVE ("acc=%s", acc->accountName);
|
||||
}
|
||||
|
||||
|
||||
/* ============================================================== */
|
||||
|
||||
/** The xaccLotFill() routine attempts to assign splits to the
|
||||
* indicated lot until the lot balance goes to zero, or until
|
||||
* there are no suitable (i.e. unassigned) splits left in the
|
||||
* account. It uses the default accounting policy to choose
|
||||
* the splits to fill out the lot.
|
||||
*/
|
||||
|
||||
void
|
||||
xaccLotFill (GNCLot *lot)
|
||||
{
|
||||
gnc_numeric lot_baln;
|
||||
Account *acc;
|
||||
|
||||
if (!lot) return;
|
||||
acc = lot->account;
|
||||
|
||||
ENTER ("acc=%s", acc->accountName);
|
||||
|
||||
/* If balance already zero, we have nothing to do. */
|
||||
lot_baln = gnc_lot_get_balance (lot);
|
||||
if (gnc_numeric_zero_p (lot_baln)) return;
|
||||
|
||||
xaccAccountBeginEdit (acc);
|
||||
|
||||
/* Loop until we've filled up the lot, (i.e. till the
|
||||
* balance goes to zero) or there are no splits left. */
|
||||
while (1)
|
||||
{
|
||||
Split *split, *subsplit;
|
||||
|
||||
split = FIFOPolicyGetSplit (lot, NULL);
|
||||
subsplit = xaccSplitAssignToLot (split, lot);
|
||||
if (subsplit == split)
|
||||
{
|
||||
PERR ("Accounting Policy gave us a split that "
|
||||
"doesn't fit into this lot");
|
||||
break;
|
||||
}
|
||||
|
||||
lot_baln = gnc_lot_get_balance (lot);
|
||||
if (gnc_numeric_zero_p (lot_baln)) break;
|
||||
}
|
||||
xaccAccountCommitEdit (acc);
|
||||
LEAVE ("acc=%s", acc->accountName);
|
||||
}
|
||||
|
||||
/* ============================================================== */
|
||||
|
||||
void
|
||||
@ -172,7 +220,7 @@ static gpointer
|
||||
lot_scrub_cb (Account *acc, gpointer data)
|
||||
{
|
||||
if (FALSE == xaccAccountHasTrades (acc)) return NULL;
|
||||
xaccAccountScrubLots (acc);
|
||||
xaccAccountAssignLots (acc);
|
||||
xaccAccountScrubDoubleBalance (acc);
|
||||
return NULL;
|
||||
}
|
||||
@ -189,7 +237,7 @@ xaccAccountScrubLotsBalance (Account *acc)
|
||||
{
|
||||
if (!acc) return;
|
||||
if (FALSE == xaccAccountHasTrades (acc)) return;
|
||||
xaccAccountScrubLots (acc);
|
||||
xaccAccountAssignLots (acc);
|
||||
xaccAccountScrubDoubleBalance (acc);
|
||||
}
|
||||
|
||||
@ -201,7 +249,7 @@ xaccAccountTreeScrubLotsBalance (Account *acc)
|
||||
xaccGroupScrubLotsBalance (acc->children);
|
||||
|
||||
if (FALSE == xaccAccountHasTrades (acc)) return;
|
||||
xaccAccountScrubLots (acc);
|
||||
xaccAccountAssignLots (acc);
|
||||
xaccAccountScrubDoubleBalance (acc);
|
||||
}
|
||||
|
||||
|
@ -56,15 +56,24 @@ void xaccGroupScrubLotsBalance (AccountGroup *grp);
|
||||
void xaccAccountScrubLotsBalance (Account *acc);
|
||||
void xaccAccountTreeScrubLotsBalance (Account *acc);
|
||||
|
||||
/** The xaccAccountScrubLots() routine will walk over all of
|
||||
/** The xaccAccountAssignLots() 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
|
||||
* to close the oldest open lot(s). If there are no open
|
||||
* lots, a new lot will be started. By trying to close the
|
||||
* oldest lots, this routine implements a FIFO acounting
|
||||
* policy.
|
||||
* to a lot. Currently, the default (and only implemented)
|
||||
* assignment policy is a FIFO policy: Any splits that are
|
||||
* not in a lot will be used to close the oldest open lot(s).
|
||||
* If there are no open lots, a new lot will be started.
|
||||
* By trying to close the oldest lots, this effectively
|
||||
* implements a FIFO acounting policy.
|
||||
*/
|
||||
void xaccAccountScrubLots (Account *acc);
|
||||
void xaccAccountAssignLots (Account *acc);
|
||||
|
||||
/** The xaccLotFill() routine attempts to assign splits to the
|
||||
* indicated lot until the lot balance goes to zero, or until
|
||||
* there are no suitable (i.e. unassigned) splits left in the
|
||||
* account. It uses the default accounting policy to choose
|
||||
* the splits to fill out the lot.
|
||||
*/
|
||||
void xaccLotFill (GNCLot *lot);
|
||||
|
||||
/** The xaccAccountScrubDoubleBalance() routine examines all
|
||||
* of the closed lots in an account, and verifies that the
|
||||
|
@ -33,13 +33,17 @@
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include "cap-gains.h"
|
||||
#include "gnc-commodity.h"
|
||||
#include "gnc-engine.h"
|
||||
#include "gnc-lot.h"
|
||||
#include "gnc-numeric.h"
|
||||
#include "gnc-trace.h"
|
||||
#include "kvp_frame.h"
|
||||
#include "kvp-util-p.h"
|
||||
#include "policy-p.h"
|
||||
#include "Account.h"
|
||||
#include "Scrub2.h"
|
||||
#include "Scrub3.h"
|
||||
#include "Transaction.h"
|
||||
#include "TransactionP.h"
|
||||
@ -178,6 +182,16 @@ merge_splits (Split *sa, Split *sb)
|
||||
* no way its reconciled. */
|
||||
xaccSplitSetReconcile (sa, NREC);
|
||||
|
||||
/* If sb has associated gains splits, trash them. */
|
||||
if ((sb->gains_split) &&
|
||||
(sb->gains_split->gains & GAINS_STATUS_GAINS))
|
||||
{
|
||||
Transaction *t = sb->gains_split->parent;
|
||||
xaccTransBeginEdit (t);
|
||||
xaccTransDestroy (t);
|
||||
xaccTransCommitEdit (t);
|
||||
}
|
||||
|
||||
/* Finally, delete sb */
|
||||
xaccSplitDestroy(sb);
|
||||
|
||||
@ -220,7 +234,7 @@ restart:
|
||||
}
|
||||
|
||||
gboolean
|
||||
xaccScrubMergeTxnSubSplits (Transaction *txn)
|
||||
xaccScrubMergeTransSubSplits (Transaction *txn)
|
||||
{
|
||||
gboolean rc = FALSE;
|
||||
SplitList *node;
|
||||
@ -241,4 +255,84 @@ restart:
|
||||
return rc;
|
||||
}
|
||||
|
||||
gboolean
|
||||
xaccScrubMergeLotSubSplits (GNCLot *lot)
|
||||
{
|
||||
gboolean rc = FALSE;
|
||||
SplitList *node;
|
||||
|
||||
if (!lot) return FALSE;
|
||||
|
||||
ENTER (" ");
|
||||
restart:
|
||||
for (node=gnc_lot_get_split_list(lot); node; node=node->next)
|
||||
{
|
||||
Split *s = node->data;
|
||||
if (!xaccScrubMergeSubSplits(s)) continue;
|
||||
|
||||
rc = TRUE;
|
||||
goto restart;
|
||||
}
|
||||
LEAVE (" splits merged=%d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* ================================================================= */
|
||||
|
||||
void
|
||||
xaccScrubLot (GNCLot *lot)
|
||||
{
|
||||
gnc_numeric lot_baln;
|
||||
gboolean opening_baln_is_pos, lot_baln_is_pos;
|
||||
Account *acc;
|
||||
|
||||
if (!lot) return;
|
||||
ENTER (" ");
|
||||
|
||||
acc = gnc_lot_get_account (lot);
|
||||
xaccAccountBeginEdit(acc);
|
||||
xaccScrubMergeLotSubSplits (lot);
|
||||
|
||||
/* If the lot balance is zero, we don't need to rebalance */
|
||||
lot_baln = gnc_lot_get_balance (lot);
|
||||
if (! gnc_numeric_zero_p (lot_baln))
|
||||
{
|
||||
SplitList *node;
|
||||
gnc_numeric opening_baln;
|
||||
|
||||
/* Get the opening balance for this lot */
|
||||
FIFOPolicyGetLotOpening (lot, &opening_baln, NULL, NULL, NULL);
|
||||
|
||||
/* If the lot is fat, give the boot to all the non-opening
|
||||
* splits, and refill it */
|
||||
opening_baln_is_pos = gnc_numeric_positive_p(opening_baln);
|
||||
lot_baln_is_pos = gnc_numeric_positive_p(lot_baln);
|
||||
if ((opening_baln_is_pos || lot_baln_is_pos) &&
|
||||
((!opening_baln_is_pos) || (!lot_baln_is_pos)))
|
||||
{
|
||||
rethin:
|
||||
for (node=gnc_lot_get_split_list(lot); node; node=node->next)
|
||||
{
|
||||
Split *s = node->data;
|
||||
if (FIFOPolicyIsOpeningSplit (lot, s, NULL)) continue;
|
||||
gnc_lot_remove_split (lot, s);
|
||||
goto rethin;
|
||||
}
|
||||
}
|
||||
|
||||
/* At this point the lot is thin, so try to fill it */
|
||||
xaccLotFill (lot);
|
||||
|
||||
/* Make sure there are no subsplits. */
|
||||
xaccScrubMergeLotSubSplits (lot);
|
||||
}
|
||||
|
||||
/* Now re-compute cap gains, and then double-check that. */
|
||||
xaccLotComputeCapGains (lot, NULL);
|
||||
xaccLotScrubDoubleBalance (lot);
|
||||
xaccAccountCommitEdit(acc);
|
||||
|
||||
LEAVE (" ");
|
||||
}
|
||||
|
||||
/* ========================== END OF FILE ========================= */
|
||||
|
@ -53,7 +53,7 @@ void xaccScrubSubSplitPrice (Split *split);
|
||||
/** The xaccScrubMergeSubSplits() routine will merge together
|
||||
* all of the splits that were at one time split off from this
|
||||
* split, but are no longer needed to be kept separate. Splits
|
||||
* migt be split up if they need to be divided over multiple
|
||||
* might be split up if they need to be divided over multiple
|
||||
* lots; they can be merged back together if the lots change.
|
||||
* In particular, two sub-splits may be merged if they are in
|
||||
* the same lot, or in no lot. Note that, by definition, all
|
||||
@ -62,11 +62,24 @@ void xaccScrubSubSplitPrice (Split *split);
|
||||
* The routine returns TRUE if a merger was performed, else
|
||||
* it returns FALSE.
|
||||
*
|
||||
* The xaccScrubMergeTxnSubSplits() routine does the same, except
|
||||
* The xaccScrubMergeTransSubSplits() routine does the same, except
|
||||
* that it does it for all of the splits in the transaction.
|
||||
* The xaccScrubMergeLotSubSplits() routine does the same, except
|
||||
* that it does it for all of the splits in the lot.
|
||||
*/
|
||||
gboolean xaccScrubMergeSubSplits (Split *split);
|
||||
gboolean xaccScrubMergeTxnSubSplits (Transaction *txn);
|
||||
gboolean xaccScrubMergeTransSubSplits (Transaction *txn);
|
||||
gboolean xaccScrubMergeLotSubSplits (GNCLot *lot);
|
||||
|
||||
/** The xaccScrubLot() routine makes sure that the indicated lot is
|
||||
* self-consistent and properly balanced, and fixes it if its not.
|
||||
* This is an important routine to call if the amount of any split
|
||||
* in the lot is changed. That's because (obviously) changing
|
||||
* split values is gaurenteed to throw off lot balances.
|
||||
* This routine may end up closing the lot, or at least trying
|
||||
* to. It will also cause cap gains to be recomputed.
|
||||
*/
|
||||
void xaccScrubLot (GNCLot *lot);
|
||||
|
||||
#endif /* XACC_SCRUB3_H */
|
||||
/** @} */
|
||||
|
@ -51,10 +51,6 @@ ToDo List:
|
||||
Ideally, later lots are dissolved, and recomputed. However, some
|
||||
lots may have been user-hand-built. These should be left alone.
|
||||
|
||||
o XXX if the split has been split, and the lots need to be recomputed,
|
||||
then the peers need to be reunified first! And that implies that
|
||||
gain transactions need to be 'reunified' too.
|
||||
|
||||
o XXX Need to create a data-integrity scrubber, tht makes sure that
|
||||
the various flags, and pointers & etc. match. See sections marked
|
||||
with XXX below for things that might go wrong.
|
||||
@ -68,6 +64,8 @@ ToDo List:
|
||||
#include "AccountP.h"
|
||||
#include "Group.h"
|
||||
#include "GroupP.h"
|
||||
#include "Scrub2.h"
|
||||
#include "Scrub3.h"
|
||||
#include "Transaction.h"
|
||||
#include "TransactionP.h"
|
||||
#include "cap-gains.h"
|
||||
@ -78,6 +76,8 @@ ToDo List:
|
||||
#include "gnc-trace.h"
|
||||
#include "kvp-util-p.h"
|
||||
#include "messages.h"
|
||||
#include "policy.h"
|
||||
#include "policy-p.h"
|
||||
#include "qofid-p.h"
|
||||
|
||||
static short module = MOD_LOT;
|
||||
@ -255,7 +255,8 @@ xaccAccountGetDefaultGainAccount (Account *acc, gnc_commodity * currency)
|
||||
|
||||
/* ============================================================== */
|
||||
/* Functionally identical to the following:
|
||||
* if (!xaccAccountGetDefaultGainAccount()) xaccAccountSetDefaultGainAccount ();
|
||||
* if (!xaccAccountGetDefaultGainAccount()) {
|
||||
* xaccAccountSetDefaultGainAccount (); }
|
||||
* except that it saves a few cycles.
|
||||
*/
|
||||
|
||||
@ -298,20 +299,189 @@ GetOrMakeGainAcct (Account *acc, gnc_commodity * currency)
|
||||
}
|
||||
|
||||
/* ============================================================== */
|
||||
|
||||
Split *
|
||||
xaccSplitAssignToLot (Split *split, GNCLot *lot)
|
||||
{
|
||||
Account *acc;
|
||||
gnc_numeric baln;
|
||||
int cmp;
|
||||
gboolean baln_is_positive, amt_is_positive;
|
||||
|
||||
if (!lot) return split;
|
||||
if (!split) return NULL;
|
||||
|
||||
/* If this split already belongs to a lot, we are done. */
|
||||
if (split->lot) return NULL;
|
||||
|
||||
/* If the lot is closed, we can't add anything to it */
|
||||
baln = gnc_lot_get_balance (lot);
|
||||
if (gnc_lot_is_closed (lot)) return split;
|
||||
|
||||
/* If the lot balance is zero, but the lot is open, then
|
||||
* the lot is empty. Unconditionally add the split. */
|
||||
if (gnc_numeric_zero_p (baln))
|
||||
{
|
||||
acc = split->acc;
|
||||
xaccAccountBeginEdit (acc);
|
||||
gnc_lot_add_split (lot, split);
|
||||
PINFO ("simple added split to lot, new lot baln=%s",
|
||||
gnc_numeric_to_string (gnc_lot_get_balance(lot)));
|
||||
xaccAccountCommitEdit (acc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* If the sign of the split is the same as the sign of the lot,
|
||||
* we won't add it, because that would make the lot bigger, not
|
||||
* smaller. Our only function here is to make lot balances smaller.
|
||||
*/
|
||||
baln_is_positive = gnc_numeric_positive_p (baln);
|
||||
amt_is_positive = gnc_numeric_positive_p (split->amount);
|
||||
if ((baln_is_positive && amt_is_positive) ||
|
||||
((!baln_is_positive) && (!amt_is_positive)))
|
||||
{
|
||||
return split;
|
||||
}
|
||||
|
||||
/* If adding the split would make the lot balance change sign,
|
||||
* then we split the split into two pieces: one piece that will
|
||||
* bring the lot balance to zero, and another to be dealt with
|
||||
* later. */
|
||||
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, ==0 if amt==baln */
|
||||
if (0 >= cmp)
|
||||
{
|
||||
acc = split->acc;
|
||||
xaccAccountBeginEdit (acc);
|
||||
gnc_lot_add_split (lot, split);
|
||||
PINFO ("simple added split to lot, new lot baln=%s",
|
||||
gnc_numeric_to_string (gnc_lot_get_balance(lot)));
|
||||
xaccAccountCommitEdit (acc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* If we are here, then (cmp == +1 iff (amt > baln)) and we need
|
||||
* to split up the split into pieces. Do it. */
|
||||
{
|
||||
time_t now = time(0);
|
||||
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;
|
||||
|
||||
acc = split->acc;
|
||||
xaccAccountBeginEdit (acc);
|
||||
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);
|
||||
|
||||
/* We do not copy the KVP tree, as it seems like a dangerous
|
||||
* thing to do. If the user wants to access stuff in the 'old'
|
||||
* kvp tree from the 'new' split, they shoudl follow the
|
||||
* 'split-lot' pointers. Yes, this is complicated, but what
|
||||
* else can one do ??
|
||||
*/
|
||||
/* Add kvp markup to indicate that these two splits used
|
||||
* to be one before being 'split'
|
||||
*/
|
||||
gnc_kvp_bag_add (split->kvp_data, "lot-split", now,
|
||||
"peer_guid", xaccSplitGetGUID (new_split),
|
||||
NULL);
|
||||
|
||||
gnc_kvp_bag_add (new_split->kvp_data, "lot-split", now,
|
||||
"peer_guid", xaccSplitGetGUID (split),
|
||||
NULL);
|
||||
|
||||
xaccSplitSetAmount (new_split, amt_b);
|
||||
xaccSplitSetValue (new_split, val_b);
|
||||
|
||||
xaccAccountInsertSplit (acc, new_split);
|
||||
xaccTransAppendSplit (trans, new_split);
|
||||
xaccTransCommitEdit (trans);
|
||||
xaccAccountCommitEdit (acc);
|
||||
return new_split;
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================================== */
|
||||
|
||||
static GNCLot *
|
||||
MakeDefaultLot (Account *acc)
|
||||
{
|
||||
GNCLot * lot;
|
||||
gint64 id;
|
||||
char buff[200];
|
||||
|
||||
lot = gnc_lot_new (acc->book);
|
||||
|
||||
/* 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);
|
||||
|
||||
return lot;
|
||||
}
|
||||
|
||||
/* Accounting-policy callback. Given an account and an amount,
|
||||
* this routine should return a lot. By implementing this as
|
||||
* a callback, we can 'easily' add other accounting policies.
|
||||
* Currently, we only implement the FIFO policy.
|
||||
*/
|
||||
typedef GNCLot * (*AccountingPolicy) (Account *,
|
||||
Split *,
|
||||
gpointer user_data);
|
||||
static gboolean
|
||||
xaccSplitAssignToLot (Split *split,
|
||||
AccountingPolicy policy, gpointer user_data)
|
||||
PolicyAssignSplit (Split *split,
|
||||
AccountingPolicyGetLot policy, gpointer user_data)
|
||||
{
|
||||
Account *acc;
|
||||
gboolean splits_added = FALSE;
|
||||
gboolean splits_split_up = FALSE;
|
||||
GNCLot *lot;
|
||||
|
||||
if (!split) return FALSE;
|
||||
@ -324,7 +494,7 @@ xaccSplitAssignToLot (Split *split,
|
||||
xaccAccountBeginEdit (acc);
|
||||
|
||||
/* If we are here, this split does not belong to any lot.
|
||||
* Lets put it in the earliest one we can find. This
|
||||
* We ask the policy for a lot to assign it to. This
|
||||
* block is written in the form of a while loop, since we
|
||||
* may have to bust a split across several lots.
|
||||
*/
|
||||
@ -332,142 +502,29 @@ xaccSplitAssignToLot (Split *split,
|
||||
{
|
||||
PINFO ("have split amount=%s", gnc_numeric_to_string (split->amount));
|
||||
split->gains |= GAINS_STATUS_VDIRTY;
|
||||
lot = policy (acc, split, user_data);
|
||||
if (lot)
|
||||
lot = policy (split, user_data);
|
||||
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)
|
||||
{
|
||||
time_t now = time(0);
|
||||
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);
|
||||
|
||||
/* We do not copy the KVP tree, as it seems like a dangerous
|
||||
* thing to do. If the user wants to access stuff in the 'old'
|
||||
* kvp tree from the 'new' split, they shoudl follow the
|
||||
* 'split-lot' pointers. Yes, this is complicated, but what
|
||||
* else can one do ??
|
||||
*/
|
||||
/* Add kvp markup to indicate that these two splits used
|
||||
* to be one before being 'split'
|
||||
*/
|
||||
gnc_kvp_bag_add (split->kvp_data, "lot-split", now,
|
||||
"peer_guid", xaccSplitGetGUID (new_split),
|
||||
NULL);
|
||||
|
||||
gnc_kvp_bag_add (new_split->kvp_data, "lot-split", now,
|
||||
"peer_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 */
|
||||
lot = MakeDefaultLot (acc);
|
||||
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);
|
||||
}
|
||||
split = xaccSplitAssignToLot (split, lot);
|
||||
if (split) splits_split_up = TRUE;
|
||||
}
|
||||
xaccAccountCommitEdit (acc);
|
||||
|
||||
LEAVE ("added=%d", splits_added);
|
||||
return splits_added;
|
||||
}
|
||||
|
||||
static GNCLot *
|
||||
FIFOPolicy (Account *acc, Split *split, gpointer user_data)
|
||||
{
|
||||
return xaccAccountFindEarliestOpenLot (acc, split->amount);
|
||||
LEAVE ("split_up=%d", splits_split_up);
|
||||
return splits_split_up;
|
||||
}
|
||||
|
||||
gboolean
|
||||
xaccSplitFIFOAssignToLot (Split *split)
|
||||
xaccSplitAssign (Split *split)
|
||||
{
|
||||
return xaccSplitAssignToLot (split, FIFOPolicy, NULL);
|
||||
/* XXX FIXME: instead of a hard-wired fifo policy, we should
|
||||
* be using the policy as specified by the account. i.e.
|
||||
* we should be using split->acc->polcy as the function
|
||||
*/
|
||||
return PolicyAssignSplit (split, FIFOPolicyGetLot, NULL);
|
||||
}
|
||||
|
||||
/* ============================================================== */
|
||||
@ -496,11 +553,12 @@ xaccSplitGetCapGainsSplit (Split *split)
|
||||
void
|
||||
xaccSplitComputeCapGains(Split *split, Account *gain_acc)
|
||||
{
|
||||
Split *opening_split;
|
||||
GNCLot *lot;
|
||||
gnc_commodity *currency = NULL;
|
||||
gnc_numeric zero = gnc_numeric_zero();
|
||||
gnc_numeric value = zero;
|
||||
gnc_numeric opening_amount, opening_value;
|
||||
gnc_commodity *opening_currency;
|
||||
|
||||
if (!split) return;
|
||||
lot = split->lot;
|
||||
@ -545,16 +603,18 @@ xaccSplitComputeCapGains(Split *split, Account *gain_acc)
|
||||
* may exist if users attempted to manually record gains. */
|
||||
if (gnc_numeric_zero_p (split->amount)) return;
|
||||
|
||||
opening_split = gnc_lot_get_earliest_split(lot);
|
||||
if (split == opening_split)
|
||||
FIFOPolicyGetLotOpening (lot, &opening_amount, &opening_value,
|
||||
&opening_currency, NULL);
|
||||
|
||||
if (FIFOPolicyIsOpeningSplit (lot, split, NULL))
|
||||
{
|
||||
#if MOVE_THIS_TO_A_DATA_INTEGRITY_SCRUBBER
|
||||
/* Check to make sure that this opening split doesn't
|
||||
* have a cap-gain transaction associated with it.
|
||||
* If it does, that's wrong, and we ruthlessly destroy it.
|
||||
* XXX Don't do this, it leads to infinite loops.
|
||||
* We need to scrub out errors like this elsewhere!
|
||||
*/
|
||||
#if MOVE_THIS_TO_A_DATA_INTEGRITY_SCRUBBER
|
||||
if (xaccSplitGetCapGainsSplit (split))
|
||||
{
|
||||
Split *gains_split = xaccSplitGetCapGainsSplit(split);
|
||||
@ -569,10 +629,9 @@ xaccSplitComputeCapGains(Split *split, Account *gain_acc)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check to make sure the opening split and this split
|
||||
/* Check to make sure the lot-opening currency and this split
|
||||
* use the same currency */
|
||||
if (FALSE == gnc_commodity_equiv (currency,
|
||||
opening_split->parent->common_currency))
|
||||
if (FALSE == gnc_commodity_equiv (currency, opening_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
|
||||
@ -583,16 +642,17 @@ xaccSplitComputeCapGains(Split *split, Account *gain_acc)
|
||||
|
||||
/* Opening amount should be larger (or equal) to current split,
|
||||
* and it should be of the opposite sign.
|
||||
XXX this should be a part of a scrub routine !
|
||||
*/
|
||||
if (0 > gnc_numeric_compare (gnc_numeric_abs(opening_split->amount),
|
||||
if (0 > gnc_numeric_compare (gnc_numeric_abs(opening_amount),
|
||||
gnc_numeric_abs(split->amount)))
|
||||
{
|
||||
PERR ("Malformed Lot! (too thin!)\n");
|
||||
return;
|
||||
}
|
||||
if ( (gnc_numeric_negative_p(opening_split->amount) ||
|
||||
if ( (gnc_numeric_negative_p(opening_amount) ||
|
||||
gnc_numeric_positive_p(split->amount)) &&
|
||||
(gnc_numeric_positive_p(opening_split->amount) ||
|
||||
(gnc_numeric_positive_p(opening_amount) ||
|
||||
gnc_numeric_negative_p(split->amount)))
|
||||
{
|
||||
PERR ("Malformed Lot! (too fat!)\n");
|
||||
@ -606,16 +666,16 @@ xaccSplitComputeCapGains(Split *split, Account *gain_acc)
|
||||
* cost_basis = purchase_price * current_amount
|
||||
* cap_gain = current_value - cost_basis
|
||||
*/
|
||||
value = gnc_numeric_mul (opening_split->value, split->amount,
|
||||
value = gnc_numeric_mul (opening_value, split->amount,
|
||||
GNC_DENOM_AUTO, GNC_RND_NEVER);
|
||||
value = gnc_numeric_div (value, opening_split->amount,
|
||||
gnc_numeric_denom(opening_split->value), GNC_DENOM_EXACT);
|
||||
value = gnc_numeric_div (value, opening_amount,
|
||||
gnc_numeric_denom(opening_value), GNC_DENOM_EXACT);
|
||||
|
||||
value = gnc_numeric_sub (value, split->value,
|
||||
GNC_DENOM_AUTO, GNC_DENOM_LCD);
|
||||
PINFO ("Open amt=%s val=%s; split amt=%s val=%s; gains=%s\n",
|
||||
gnc_numeric_to_string (opening_split->amount),
|
||||
gnc_numeric_to_string (opening_split->value),
|
||||
gnc_numeric_to_string (opening_amount),
|
||||
gnc_numeric_to_string (opening_value),
|
||||
gnc_numeric_to_string (split->amount),
|
||||
gnc_numeric_to_string (split->value),
|
||||
gnc_numeric_to_string (value));
|
||||
@ -626,7 +686,7 @@ xaccSplitComputeCapGains(Split *split, Account *gain_acc)
|
||||
* 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))
|
||||
if (FALSE == gnc_numeric_zero_p (value))
|
||||
{
|
||||
Transaction *trans;
|
||||
Split *lot_split, *gain_split;
|
||||
@ -752,4 +812,18 @@ xaccSplitGetCapGains(Split * split)
|
||||
return split->value;
|
||||
}
|
||||
|
||||
/* ============================================================== */
|
||||
|
||||
void
|
||||
xaccLotComputeCapGains (GNCLot *lot, Account *gain_acc)
|
||||
{
|
||||
SplitList *node;
|
||||
|
||||
for (node=lot->splits; node; node=node->next)
|
||||
{
|
||||
Split *s = node->data;
|
||||
xaccSplitComputeCapGains (s, gain_acc);
|
||||
}
|
||||
}
|
||||
|
||||
/* =========================== END OF FILE ======================= */
|
||||
|
@ -44,6 +44,7 @@
|
||||
#define XACC_CAP_GAINS_H
|
||||
|
||||
#include "gnc-engine.h"
|
||||
#include "gnc-numeric.h"
|
||||
|
||||
/** The xaccSplitGetCapGains() method returns the value of
|
||||
* capital gains (if any) associated with the indicated
|
||||
@ -107,12 +108,13 @@ void xaccAccountSetDefaultGainAccount (Account *acc, Account *gains_acct);
|
||||
|
||||
Split * xaccSplitGetCapGainsSplit (Split *);
|
||||
|
||||
/** The`xaccSplitFIFOAssignToLot() routine will take the indicated
|
||||
* split and assign it to the earliest open lot that it can find.
|
||||
/** 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 entrety, then the split
|
||||
* 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.
|
||||
@ -123,12 +125,33 @@ Split * xaccSplitGetCapGainsSplit (Split *);
|
||||
* directory is used to identify the peers. 'gemini'-style kvp's
|
||||
* are used.
|
||||
*
|
||||
* Because this routine always uses the earliest open lot, it
|
||||
* implments a "FIFO" First-In First-Out accounting policy.
|
||||
* (Adding new policies is 'easy', read the source luke).
|
||||
* 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.
|
||||
*/
|
||||
|
||||
gboolean xaccSplitFIFOAssignToLot (Split *split);
|
||||
gboolean xaccSplitAssign (Split *split);
|
||||
|
||||
/** 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.
|
||||
*/
|
||||
Split * xaccSplitAssignToLot (Split *split, GNCLot *lot);
|
||||
|
||||
/** The xaccSplitComputeCapGains() routine computes the cap gains
|
||||
* or losses for the indicated split. The gains are placed into
|
||||
@ -144,9 +167,13 @@ gboolean xaccSplitFIFOAssignToLot (Split *split);
|
||||
* 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.
|
||||
*/
|
||||
|
||||
void xaccSplitComputeCapGains(Split *split, Account *gain_acc);
|
||||
void xaccLotComputeCapGains (GNCLot *lot, Account *gain_acc);
|
||||
|
||||
#endif /* XACC_CAP_GAINS_H */
|
||||
/** @} */
|
||||
|
@ -196,7 +196,11 @@ gnc_lot_get_balance (GNCLot *lot)
|
||||
gnc_numeric baln = zero;
|
||||
if (!lot) return zero;
|
||||
|
||||
if (!lot->splits) return zero;
|
||||
if (!lot->splits)
|
||||
{
|
||||
lot->is_closed = FALSE;
|
||||
return zero;
|
||||
}
|
||||
|
||||
/* Sum over splits; because they all belong to same account
|
||||
* they will have same denominator.
|
||||
|
@ -55,6 +55,10 @@ QofBook * gnc_lot_get_book (GNCLot *);
|
||||
|
||||
/** The gnc_lot_add_split() routine adds a split to this lot. Note
|
||||
* that *all* splits in a lot must also be in the same account.
|
||||
* Note that this routine adds the split unconditionally, with
|
||||
* no regard for the accounting policy. To enforce a particular
|
||||
* accounting polciy, use the xaccSplitAssignToLot() routine
|
||||
* instead.
|
||||
*/
|
||||
void gnc_lot_add_split (GNCLot *, Split *);
|
||||
void gnc_lot_remove_split (GNCLot *, Split *);
|
||||
|
75
src/engine/policy-p.h
Normal file
75
src/engine/policy-p.h
Normal file
@ -0,0 +1,75 @@
|
||||
/********************************************************************\
|
||||
* policy-.h -- Implement Accounting Policy Private Header File *
|
||||
* *
|
||||
* 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 *
|
||||
\********************************************************************/
|
||||
|
||||
/** @file policy-p.h
|
||||
* @breif Implement Accounting Policy Private header File.
|
||||
* @author Created by Linas Vepstas August 2003
|
||||
* @author Copyright (c) 2003 Linas Vepstas <linas@linas.org>
|
||||
*
|
||||
* This file implements Accounting Policy. The Accounting Polciy
|
||||
* determines how splits are assigned to lots. The default policy
|
||||
* is teh FIFO policy: the first thing bought is also the first
|
||||
* thing sold.
|
||||
*/
|
||||
|
||||
#ifndef XACC_POLICY_P_H
|
||||
#define XACC_POLICY_P_H
|
||||
|
||||
#include "gnc-engine.h"
|
||||
|
||||
/* ============================================================== */
|
||||
/** The FIFOPolicy routines try to encapsulate the FIFO-specific
|
||||
* parts of the cap-gains routine, and can eb replaced by something
|
||||
* else for other policies (e.g. LIFO)
|
||||
*
|
||||
* The FIFOPolicyGetLot() routine returns a lot into which the
|
||||
* indicated split should be placed.
|
||||
*
|
||||
* The FIFOPolicyGetSplit() routine returns an unassinged split
|
||||
* from the account that is appropriate for placing into the
|
||||
* indicated lot. For the FIFO policy, that would be the
|
||||
* earliest split that is not in any account, and is of the
|
||||
* appropriate sign.
|
||||
*
|
||||
* The FIFOPolicyIsOpeningSplit() predicate returns a true/false
|
||||
* value, indicating if the indicated split was used to 'open'
|
||||
* or 'grow' the lot.
|
||||
*
|
||||
* The FIFOPolicyGetLotOpening() routine returns information about
|
||||
* the opening balances for the lot. The 'opening balances'
|
||||
* are the sum of all the splits used to grow (increase the size
|
||||
* of) the lot. For a FIFO polciy, there is only one split
|
||||
* that opens a lot.
|
||||
*/
|
||||
|
||||
GNCLot * FIFOPolicyGetLot (Split *split, gpointer user_data);
|
||||
|
||||
Split * FIFOPolicyGetSplit (GNCLot *lot, gpointer user_data);
|
||||
|
||||
void FIFOPolicyGetLotOpening (GNCLot *lot,
|
||||
gnc_numeric *ret_amount, gnc_numeric *ret_value,
|
||||
gnc_commodity **ret_currency,
|
||||
gpointer user_data);
|
||||
|
||||
gboolean FIFOPolicyIsOpeningSplit (GNCLot *lot, Split *split,
|
||||
gpointer user_data);
|
||||
|
||||
#endif /* XACC_POLICY_P_H */
|
103
src/engine/policy.c
Normal file
103
src/engine/policy.c
Normal file
@ -0,0 +1,103 @@
|
||||
/********************************************************************\
|
||||
* policy.c -- Implement FIFO Accounting Policy *
|
||||
* *
|
||||
* 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 *
|
||||
\********************************************************************/
|
||||
|
||||
/** @file policy.c
|
||||
* @breif Implement FIFO Accounting Policy.
|
||||
* @author Created by Linas Vepstas August 2003
|
||||
* @author Copyright (c) 2003 Linas Vepstas <linas@linas.org>
|
||||
*
|
||||
* This file implements the FIFO Accounting Policy (and, in the
|
||||
* future, others as well). The Accounting Polciy determines
|
||||
* how splits are assigned to lots.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include "Account.h"
|
||||
#include "Transaction.h"
|
||||
#include "TransactionP.h"
|
||||
#include "cap-gains.h"
|
||||
#include "gnc-engine.h"
|
||||
#include "gnc-lot.h"
|
||||
#include "gnc-lot-p.h"
|
||||
#include "gnc-trace.h"
|
||||
#include "policy.h"
|
||||
#include "policy-p.h"
|
||||
|
||||
/* static short module = MOD_LOT; */
|
||||
|
||||
/* ============================================================== */
|
||||
|
||||
GNCLot *
|
||||
FIFOPolicyGetLot (Split *split, gpointer user_data)
|
||||
{
|
||||
return xaccAccountFindEarliestOpenLot (split->acc, split->amount);
|
||||
}
|
||||
|
||||
Split *
|
||||
FIFOPolicyGetSplit (GNCLot *lot, gpointer user_data)
|
||||
{
|
||||
SplitList *node;
|
||||
gboolean want_positive;
|
||||
|
||||
want_positive = gnc_numeric_negative_p (gnc_lot_get_balance (lot));
|
||||
|
||||
/* Make use of the fact that the splits in a lot are already
|
||||
* in date order; so we don't have to search for the earliest. */
|
||||
for (node = xaccAccountGetSplitList (lot->account); node; node=node->next)
|
||||
{
|
||||
gboolean is_positive;
|
||||
Split *split = node->data;
|
||||
if (split->lot) continue;
|
||||
|
||||
is_positive = gnc_numeric_positive_p (split->amount);
|
||||
if ((want_positive && is_positive) ||
|
||||
((!want_positive) && (!is_positive))) return split;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
FIFOPolicyGetLotOpening (GNCLot *lot,
|
||||
gnc_numeric *ret_amount, gnc_numeric *ret_value,
|
||||
gnc_commodity **ret_currency,
|
||||
gpointer user_data)
|
||||
{
|
||||
Split *opening_split;
|
||||
opening_split = gnc_lot_get_earliest_split(lot);
|
||||
|
||||
if (ret_amount) *ret_amount = opening_split->amount;
|
||||
if (ret_value) *ret_value = opening_split->value;
|
||||
if (ret_currency) *ret_currency = opening_split->parent->common_currency;
|
||||
}
|
||||
|
||||
gboolean
|
||||
FIFOPolicyIsOpeningSplit (GNCLot *lot, Split *split, gpointer user_data)
|
||||
{
|
||||
Split *opening_split;
|
||||
opening_split = gnc_lot_get_earliest_split(lot);
|
||||
return (split == opening_split);
|
||||
}
|
||||
|
||||
|
||||
/* =========================== END OF FILE ======================= */
|
45
src/engine/policy.h
Normal file
45
src/engine/policy.h
Normal file
@ -0,0 +1,45 @@
|
||||
/********************************************************************\
|
||||
* policy.h -- Implement Accounting Policy *
|
||||
* *
|
||||
* 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 *
|
||||
\********************************************************************/
|
||||
|
||||
/** @file policy.h
|
||||
* @breif Implement Accounting Policy.
|
||||
* @author Created by Linas Vepstas August 2003
|
||||
* @author Copyright (c) 2003 Linas Vepstas <linas@linas.org>
|
||||
*
|
||||
* This file implements Accounting Policy. The Accounting Polciy
|
||||
* determines how splits are assigned to lots. The default policy
|
||||
* is teh FIFO policy: the first thing bought is also the first
|
||||
* thing sold.
|
||||
*/
|
||||
|
||||
#ifndef XACC_POLICY_H
|
||||
#define XACC_POLICY_H
|
||||
|
||||
#include "gnc-engine.h"
|
||||
|
||||
/* Accounting-policy callback. Given an account and an amount,
|
||||
* this routine should return a lot. By implementing this as
|
||||
* a callback, we can 'easily' add other accounting policies.
|
||||
* Currently, we only implement the FIFO policy.
|
||||
*/
|
||||
typedef GNCLot * (*AccountingPolicyGetLot) (Split *,
|
||||
gpointer user_data);
|
||||
#endif /* XACC_POLICY_H */
|
Loading…
Reference in New Issue
Block a user