mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
quick whack to add LIFO accounting policy (in addition to FIFO)
git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@9410 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
parent
33330e6b95
commit
697846331a
@ -43,6 +43,7 @@
|
|||||||
#include "kvp_frame.h"
|
#include "kvp_frame.h"
|
||||||
#include "kvp-util-p.h"
|
#include "kvp-util-p.h"
|
||||||
#include "messages.h"
|
#include "messages.h"
|
||||||
|
#include "policy.h"
|
||||||
|
|
||||||
#include "qofbackend.h"
|
#include "qofbackend.h"
|
||||||
#include "qofbackend-p.h"
|
#include "qofbackend-p.h"
|
||||||
@ -117,6 +118,7 @@ xaccInitAccount (Account * acc, QofBook *book)
|
|||||||
|
|
||||||
acc->splits = NULL;
|
acc->splits = NULL;
|
||||||
acc->lots = NULL;
|
acc->lots = NULL;
|
||||||
|
acc->policy = xaccGetFIFOPolicy();
|
||||||
|
|
||||||
acc->version = 0;
|
acc->version = 0;
|
||||||
acc->version_check = 0;
|
acc->version_check = 0;
|
||||||
|
@ -46,6 +46,7 @@
|
|||||||
#include "gnc-engine.h"
|
#include "gnc-engine.h"
|
||||||
#include "gnc-numeric.h"
|
#include "gnc-numeric.h"
|
||||||
#include "kvp_frame.h"
|
#include "kvp_frame.h"
|
||||||
|
#include "policy.h"
|
||||||
#include "qofbackend.h"
|
#include "qofbackend.h"
|
||||||
#include "qofbook.h"
|
#include "qofbook.h"
|
||||||
#include "qofid.h"
|
#include "qofid.h"
|
||||||
@ -130,6 +131,9 @@ struct account_s
|
|||||||
SplitList *splits; /* list of split pointers */
|
SplitList *splits; /* list of split pointers */
|
||||||
LotList *lots; /* list of lot pointers */
|
LotList *lots; /* list of lot pointers */
|
||||||
|
|
||||||
|
/* Cached pointer to policy method */
|
||||||
|
GNCPolicy *policy;
|
||||||
|
|
||||||
/* keep track of nesting level of begin/end edit calls */
|
/* keep track of nesting level of begin/end edit calls */
|
||||||
gint32 editlevel;
|
gint32 editlevel;
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
* Provides a set of functions and utilities for checking and
|
* Provides a set of functions and utilities for checking and
|
||||||
* repairing ('scrubbing clean') the usage of Lots and lot balances
|
* repairing ('scrubbing clean') the usage of Lots and lot balances
|
||||||
* in stock and commodity accounts. Broken lots are repaired using
|
* in stock and commodity accounts. Broken lots are repaired using
|
||||||
* a first-in, first-out (FIFO) accounting schedule.
|
* the accounts specific accounting policy (probably FIFO).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
@ -99,9 +99,11 @@ xaccLotFill (GNCLot *lot)
|
|||||||
gnc_numeric lot_baln;
|
gnc_numeric lot_baln;
|
||||||
Account *acc;
|
Account *acc;
|
||||||
Split *split;
|
Split *split;
|
||||||
|
GNCPolicy *pcy;
|
||||||
|
|
||||||
if (!lot) return;
|
if (!lot) return;
|
||||||
acc = lot->account;
|
acc = lot->account;
|
||||||
|
pcy = acc->policy;
|
||||||
|
|
||||||
ENTER ("acc=%s", acc->accountName);
|
ENTER ("acc=%s", acc->accountName);
|
||||||
|
|
||||||
@ -109,7 +111,7 @@ xaccLotFill (GNCLot *lot)
|
|||||||
lot_baln = gnc_lot_get_balance (lot);
|
lot_baln = gnc_lot_get_balance (lot);
|
||||||
if (gnc_numeric_zero_p (lot_baln)) return;
|
if (gnc_numeric_zero_p (lot_baln)) return;
|
||||||
|
|
||||||
split = FIFOPolicyGetSplit (lot, NULL);
|
split = pcy->PolicyGetSplit (pcy, lot);
|
||||||
if (!split) return; /* Handle the common case */
|
if (!split) return; /* Handle the common case */
|
||||||
|
|
||||||
xaccAccountBeginEdit (acc);
|
xaccAccountBeginEdit (acc);
|
||||||
@ -131,7 +133,7 @@ xaccLotFill (GNCLot *lot)
|
|||||||
lot_baln = gnc_lot_get_balance (lot);
|
lot_baln = gnc_lot_get_balance (lot);
|
||||||
if (gnc_numeric_zero_p (lot_baln)) break;
|
if (gnc_numeric_zero_p (lot_baln)) break;
|
||||||
|
|
||||||
split = FIFOPolicyGetSplit (lot, NULL);
|
split = pcy->PolicyGetSplit (pcy, lot);
|
||||||
if (!split) break;
|
if (!split) break;
|
||||||
}
|
}
|
||||||
xaccAccountCommitEdit (acc);
|
xaccAccountCommitEdit (acc);
|
||||||
|
@ -61,11 +61,13 @@ xaccScrubLot (GNCLot *lot)
|
|||||||
gnc_numeric lot_baln;
|
gnc_numeric lot_baln;
|
||||||
gboolean opening_baln_is_pos, lot_baln_is_pos;
|
gboolean opening_baln_is_pos, lot_baln_is_pos;
|
||||||
Account *acc;
|
Account *acc;
|
||||||
|
GNCPolicy *pcy;
|
||||||
|
|
||||||
if (!lot) return FALSE;
|
if (!lot) return FALSE;
|
||||||
ENTER (" ");
|
ENTER (" ");
|
||||||
|
|
||||||
acc = gnc_lot_get_account (lot);
|
acc = gnc_lot_get_account (lot);
|
||||||
|
pcy = acc->policy;
|
||||||
xaccAccountBeginEdit(acc);
|
xaccAccountBeginEdit(acc);
|
||||||
xaccScrubMergeLotSubSplits (lot);
|
xaccScrubMergeLotSubSplits (lot);
|
||||||
|
|
||||||
@ -77,7 +79,7 @@ xaccScrubLot (GNCLot *lot)
|
|||||||
gnc_numeric opening_baln;
|
gnc_numeric opening_baln;
|
||||||
|
|
||||||
/* Get the opening balance for this lot */
|
/* Get the opening balance for this lot */
|
||||||
FIFOPolicyGetLotOpening (lot, &opening_baln, NULL, NULL, NULL);
|
pcy->PolicyGetLotOpening (pcy, lot, &opening_baln, NULL, NULL);
|
||||||
|
|
||||||
/* If the lot is fat, give the boot to all the non-opening
|
/* If the lot is fat, give the boot to all the non-opening
|
||||||
* splits, and refill it */
|
* splits, and refill it */
|
||||||
@ -90,7 +92,7 @@ rethin:
|
|||||||
for (node=gnc_lot_get_split_list(lot); node; node=node->next)
|
for (node=gnc_lot_get_split_list(lot); node; node=node->next)
|
||||||
{
|
{
|
||||||
Split *s = node->data;
|
Split *s = node->data;
|
||||||
if (FIFOPolicyIsOpeningSplit (lot, s, NULL)) continue;
|
if (pcy->PolicyIsOpeningSplit (pcy, lot, s)) continue;
|
||||||
gnc_lot_remove_split (lot, s);
|
gnc_lot_remove_split (lot, s);
|
||||||
goto rethin;
|
goto rethin;
|
||||||
}
|
}
|
||||||
|
@ -107,16 +107,32 @@ xaccAccountHasTrades (Account *acc)
|
|||||||
|
|
||||||
/* ============================================================== */
|
/* ============================================================== */
|
||||||
|
|
||||||
struct early_lot_s
|
struct find_lot_s
|
||||||
{
|
{
|
||||||
GNCLot *lot;
|
GNCLot *lot;
|
||||||
Timespec ts;
|
Timespec ts;
|
||||||
int (*numeric_pred)(gnc_numeric);
|
int (*numeric_pred)(gnc_numeric);
|
||||||
|
gboolean (*date_pred)(Timespec e, Timespec tr);
|
||||||
};
|
};
|
||||||
|
|
||||||
static gpointer earliest_helper (GNCLot *lot, gpointer user_data)
|
static gboolean
|
||||||
|
earliest_pred (Timespec earl, Timespec tran)
|
||||||
{
|
{
|
||||||
struct early_lot_s *els = user_data;
|
return ((earl.tv_sec > tran.tv_sec) ||
|
||||||
|
((earl.tv_sec == tran.tv_sec) && (earl.tv_nsec > tran.tv_nsec)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
latest_pred (Timespec earl, Timespec tran)
|
||||||
|
{
|
||||||
|
return ((earl.tv_sec < tran.tv_sec) ||
|
||||||
|
((earl.tv_sec == tran.tv_sec) && (earl.tv_nsec < tran.tv_nsec)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static gpointer
|
||||||
|
finder_helper (GNCLot *lot, gpointer user_data)
|
||||||
|
{
|
||||||
|
struct find_lot_s *els = user_data;
|
||||||
Split *s;
|
Split *s;
|
||||||
Transaction *trans;
|
Transaction *trans;
|
||||||
gnc_numeric bal;
|
gnc_numeric bal;
|
||||||
@ -129,9 +145,7 @@ static gpointer earliest_helper (GNCLot *lot, gpointer user_data)
|
|||||||
|
|
||||||
s = gnc_lot_get_earliest_split (lot);
|
s = gnc_lot_get_earliest_split (lot);
|
||||||
trans = s->parent;
|
trans = s->parent;
|
||||||
if ((els->ts.tv_sec > trans->date_posted.tv_sec) ||
|
if (els->date_pred (els->ts, trans->date_posted))
|
||||||
((els->ts.tv_sec == trans->date_posted.tv_sec) &&
|
|
||||||
(els->ts.tv_nsec > trans->date_posted.tv_nsec)))
|
|
||||||
{
|
{
|
||||||
els->ts = trans->date_posted;
|
els->ts = trans->date_posted;
|
||||||
els->lot = lot;
|
els->lot = lot;
|
||||||
@ -140,24 +154,49 @@ static gpointer earliest_helper (GNCLot *lot, gpointer user_data)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
GNCLot *
|
static inline GNCLot *
|
||||||
xaccAccountFindEarliestOpenLot (Account *acc, gnc_numeric sign)
|
xaccAccountFindOpenLot (Account *acc, gnc_numeric sign,
|
||||||
|
long long guess,
|
||||||
|
gboolean (*date_pred)(Timespec, Timespec))
|
||||||
{
|
{
|
||||||
struct early_lot_s es;
|
struct find_lot_s es;
|
||||||
|
|
||||||
ENTER (" sign=%lld/%lld", sign.num, sign.denom);
|
|
||||||
es.lot = NULL;
|
es.lot = NULL;
|
||||||
es.ts.tv_sec = 10000000LL * ((long long) LONG_MAX);
|
es.ts.tv_sec = guess;
|
||||||
es.ts.tv_nsec = 0;
|
es.ts.tv_nsec = 0;
|
||||||
|
es.date_pred = date_pred;
|
||||||
|
|
||||||
if (gnc_numeric_positive_p(sign)) es.numeric_pred = gnc_numeric_negative_p;
|
if (gnc_numeric_positive_p(sign)) es.numeric_pred = gnc_numeric_negative_p;
|
||||||
else es.numeric_pred = gnc_numeric_positive_p;
|
else es.numeric_pred = gnc_numeric_positive_p;
|
||||||
|
|
||||||
xaccAccountForEachLot (acc, earliest_helper, &es);
|
xaccAccountForEachLot (acc, finder_helper, &es);
|
||||||
LEAVE ("found lot=%p %s", es.lot, gnc_lot_get_title (es.lot));
|
|
||||||
return es.lot;
|
return es.lot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GNCLot *
|
||||||
|
xaccAccountFindEarliestOpenLot (Account *acc, gnc_numeric sign)
|
||||||
|
{
|
||||||
|
GNCLot *lot;
|
||||||
|
ENTER (" sign=%lld/%lld", sign.num, sign.denom);
|
||||||
|
|
||||||
|
lot = xaccAccountFindOpenLot (acc, sign,
|
||||||
|
10000000LL * ((long long) LONG_MAX), earliest_pred);
|
||||||
|
LEAVE ("found lot=%p %s", lot, gnc_lot_get_title (lot));
|
||||||
|
return lot;
|
||||||
|
}
|
||||||
|
|
||||||
|
GNCLot *
|
||||||
|
xaccAccountFindLatestOpenLot (Account *acc, gnc_numeric sign)
|
||||||
|
{
|
||||||
|
GNCLot *lot;
|
||||||
|
ENTER (" sign=%lld/%lld", sign.num, sign.denom);
|
||||||
|
|
||||||
|
lot = xaccAccountFindOpenLot (acc, sign,
|
||||||
|
-10000000LL * ((long long) LONG_MAX), latest_pred);
|
||||||
|
LEAVE ("found lot=%p %s", lot, gnc_lot_get_title (lot));
|
||||||
|
return lot;
|
||||||
|
}
|
||||||
|
|
||||||
/* ============================================================== */
|
/* ============================================================== */
|
||||||
/* Similar to GetOrMakeAccount, but different in important ways */
|
/* Similar to GetOrMakeAccount, but different in important ways */
|
||||||
|
|
||||||
@ -476,15 +515,14 @@ MakeDefaultLot (Account *acc)
|
|||||||
/* Accounting-policy callback. Given an account and an amount,
|
/* Accounting-policy callback. Given an account and an amount,
|
||||||
* this routine should return a lot. By implementing this as
|
* this routine should return a lot. By implementing this as
|
||||||
* a callback, we can 'easily' add other accounting policies.
|
* a callback, we can 'easily' add other accounting policies.
|
||||||
* Currently, we only implement the FIFO policy.
|
|
||||||
*/
|
*/
|
||||||
static gboolean
|
gboolean
|
||||||
PolicyAssignSplit (Split *split,
|
xaccSplitAssign (Split *split)
|
||||||
AccountingPolicyGetLot policy, gpointer user_data)
|
|
||||||
{
|
{
|
||||||
Account *acc;
|
Account *acc;
|
||||||
gboolean splits_split_up = FALSE;
|
gboolean splits_split_up = FALSE;
|
||||||
GNCLot *lot;
|
GNCLot *lot;
|
||||||
|
GNCPolicy *pcy;
|
||||||
|
|
||||||
if (!split) return FALSE;
|
if (!split) return FALSE;
|
||||||
|
|
||||||
@ -493,6 +531,7 @@ PolicyAssignSplit (Split *split,
|
|||||||
/* If this split already belongs to a lot, we are done. */
|
/* If this split already belongs to a lot, we are done. */
|
||||||
if (split->lot) return FALSE;
|
if (split->lot) return FALSE;
|
||||||
acc = split->acc;
|
acc = split->acc;
|
||||||
|
pcy = acc->policy;
|
||||||
xaccAccountBeginEdit (acc);
|
xaccAccountBeginEdit (acc);
|
||||||
|
|
||||||
/* If we are here, this split does not belong to any lot.
|
/* If we are here, this split does not belong to any lot.
|
||||||
@ -504,7 +543,7 @@ PolicyAssignSplit (Split *split,
|
|||||||
{
|
{
|
||||||
PINFO ("have split amount=%s", gnc_numeric_to_string (split->amount));
|
PINFO ("have split amount=%s", gnc_numeric_to_string (split->amount));
|
||||||
split->gains |= GAINS_STATUS_VDIRTY;
|
split->gains |= GAINS_STATUS_VDIRTY;
|
||||||
lot = policy (split, user_data);
|
lot = pcy->PolicyGetLot (pcy, split);
|
||||||
if (!lot)
|
if (!lot)
|
||||||
{
|
{
|
||||||
lot = MakeDefaultLot (acc);
|
lot = MakeDefaultLot (acc);
|
||||||
@ -519,16 +558,6 @@ PolicyAssignSplit (Split *split,
|
|||||||
return splits_split_up;
|
return splits_split_up;
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean
|
|
||||||
xaccSplitAssign (Split *split)
|
|
||||||
{
|
|
||||||
/* 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================================== */
|
/* ============================================================== */
|
||||||
|
|
||||||
Split *
|
Split *
|
||||||
@ -558,6 +587,7 @@ xaccSplitComputeCapGains(Split *split, Account *gain_acc)
|
|||||||
{
|
{
|
||||||
SplitList *node;
|
SplitList *node;
|
||||||
GNCLot *lot;
|
GNCLot *lot;
|
||||||
|
GNCPolicy *pcy;
|
||||||
gnc_commodity *currency = NULL;
|
gnc_commodity *currency = NULL;
|
||||||
gnc_numeric zero = gnc_numeric_zero();
|
gnc_numeric zero = gnc_numeric_zero();
|
||||||
gnc_numeric value = zero;
|
gnc_numeric value = zero;
|
||||||
@ -567,6 +597,7 @@ xaccSplitComputeCapGains(Split *split, Account *gain_acc)
|
|||||||
if (!split) return;
|
if (!split) return;
|
||||||
lot = split->lot;
|
lot = split->lot;
|
||||||
if (!lot) return;
|
if (!lot) return;
|
||||||
|
pcy = lot->account->policy;
|
||||||
currency = split->parent->common_currency;
|
currency = split->parent->common_currency;
|
||||||
|
|
||||||
ENTER ("split=%p gains=%p status=0x%x lot=%s", split,
|
ENTER ("split=%p gains=%p status=0x%x lot=%s", split,
|
||||||
@ -575,7 +606,7 @@ xaccSplitComputeCapGains(Split *split, Account *gain_acc)
|
|||||||
|
|
||||||
/* Make sure the status flags and pointers are initialized */
|
/* Make sure the status flags and pointers are initialized */
|
||||||
if (GAINS_STATUS_UNKNOWN == split->gains) xaccSplitDetermineGainStatus(split);
|
if (GAINS_STATUS_UNKNOWN == split->gains) xaccSplitDetermineGainStatus(split);
|
||||||
if (FIFOPolicyIsOpeningSplit (lot, split, NULL))
|
if (pcy->PolicyIsOpeningSplit (pcy, lot, split))
|
||||||
{
|
{
|
||||||
#if MOVE_THIS_TO_A_DATA_INTEGRITY_SCRUBBER
|
#if MOVE_THIS_TO_A_DATA_INTEGRITY_SCRUBBER
|
||||||
/* Check to make sure that this opening split doesn't
|
/* Check to make sure that this opening split doesn't
|
||||||
@ -629,7 +660,7 @@ xaccSplitComputeCapGains(Split *split, Account *gain_acc)
|
|||||||
for (node=lot->splits; node; node=node->next)
|
for (node=lot->splits; node; node=node->next)
|
||||||
{
|
{
|
||||||
Split *s = node->data;
|
Split *s = node->data;
|
||||||
if (FIFOPolicyIsOpeningSplit(lot,s,NULL))
|
if (pcy->PolicyIsOpeningSplit(pcy,lot,s))
|
||||||
{
|
{
|
||||||
if (GAINS_STATUS_UNKNOWN == s->gains) xaccSplitDetermineGainStatus (s);
|
if (GAINS_STATUS_UNKNOWN == s->gains) xaccSplitDetermineGainStatus (s);
|
||||||
if (s->gains & GAINS_STATUS_VDIRTY)
|
if (s->gains & GAINS_STATUS_VDIRTY)
|
||||||
@ -655,8 +686,8 @@ xaccSplitComputeCapGains(Split *split, Account *gain_acc)
|
|||||||
* 'dirty' and the gains really do need to be recomputed.
|
* 'dirty' and the gains really do need to be recomputed.
|
||||||
* So start working things. */
|
* So start working things. */
|
||||||
|
|
||||||
FIFOPolicyGetLotOpening (lot, &opening_amount, &opening_value,
|
pcy->PolicyGetLotOpening (pcy, lot, &opening_amount, &opening_value,
|
||||||
&opening_currency, NULL);
|
&opening_currency);
|
||||||
|
|
||||||
/* Check to make sure the lot-opening currency and this split
|
/* Check to make sure the lot-opening currency and this split
|
||||||
* use the same currency */
|
* use the same currency */
|
||||||
@ -849,16 +880,18 @@ void
|
|||||||
xaccLotComputeCapGains (GNCLot *lot, Account *gain_acc)
|
xaccLotComputeCapGains (GNCLot *lot, Account *gain_acc)
|
||||||
{
|
{
|
||||||
SplitList *node;
|
SplitList *node;
|
||||||
|
GNCPolicy *pcy;
|
||||||
gboolean is_dirty = FALSE;
|
gboolean is_dirty = FALSE;
|
||||||
|
|
||||||
/* Note: if the value of the 'opening' split(s) has changed,
|
/* Note: if the value of the 'opening' split(s) has changed,
|
||||||
* then the cap gains are changed. To capture this, we need
|
* then the cap gains are changed. To capture this, we need
|
||||||
* to mark all splits dirty if the opening splits are dirty. */
|
* to mark all splits dirty if the opening splits are dirty. */
|
||||||
|
|
||||||
|
pcy = lot->account->policy;
|
||||||
for (node=lot->splits; node; node=node->next)
|
for (node=lot->splits; node; node=node->next)
|
||||||
{
|
{
|
||||||
Split *s = node->data;
|
Split *s = node->data;
|
||||||
if (FIFOPolicyIsOpeningSplit(lot,s,NULL))
|
if (pcy->PolicyIsOpeningSplit(pcy,lot,s))
|
||||||
{
|
{
|
||||||
if (GAINS_STATUS_UNKNOWN == s->gains) xaccSplitDetermineGainStatus (s);
|
if (GAINS_STATUS_UNKNOWN == s->gains) xaccSplitDetermineGainStatus (s);
|
||||||
if (s->gains & GAINS_STATUS_VDIRTY)
|
if (s->gains & GAINS_STATUS_VDIRTY)
|
||||||
|
@ -80,6 +80,7 @@ gboolean xaccAccountHasTrades (Account *);
|
|||||||
* that the balance only decreases.
|
* that the balance only decreases.
|
||||||
*/
|
*/
|
||||||
GNCLot * xaccAccountFindEarliestOpenLot (Account *acc, gnc_numeric sign);
|
GNCLot * xaccAccountFindEarliestOpenLot (Account *acc, gnc_numeric sign);
|
||||||
|
GNCLot * xaccAccountFindLatestOpenLot (Account *acc, gnc_numeric sign);
|
||||||
|
|
||||||
/** The xaccAccountGetDefaultGainAccount() routine will return
|
/** The xaccAccountGetDefaultGainAccount() routine will return
|
||||||
* the account to which realized gains/losses may be posted.
|
* the account to which realized gains/losses may be posted.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/********************************************************************\
|
/********************************************************************\
|
||||||
* policy-.h -- Implement Accounting Policy Private Header File *
|
* policy-p.h -- Implement Accounting Policy Private Header File *
|
||||||
* *
|
* *
|
||||||
* This program is free software; you can redistribute it and/or *
|
* This program is free software; you can redistribute it and/or *
|
||||||
* modify it under the terms of the GNU General Public License as *
|
* modify it under the terms of the GNU General Public License as *
|
||||||
@ -24,9 +24,9 @@
|
|||||||
* @author Created by Linas Vepstas August 2003
|
* @author Created by Linas Vepstas August 2003
|
||||||
* @author Copyright (c) 2003 Linas Vepstas <linas@linas.org>
|
* @author Copyright (c) 2003 Linas Vepstas <linas@linas.org>
|
||||||
*
|
*
|
||||||
* This file implements Accounting Policy. The Accounting Polciy
|
* This file implements Accounting Policy. The Accounting Policy
|
||||||
* determines how splits are assigned to lots. The default policy
|
* determines how splits are assigned to lots. The default policy
|
||||||
* is teh FIFO policy: the first thing bought is also the first
|
* is the FIFO policy: the first thing bought is also the first
|
||||||
* thing sold.
|
* thing sold.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -34,42 +34,44 @@
|
|||||||
#define XACC_POLICY_P_H
|
#define XACC_POLICY_P_H
|
||||||
|
|
||||||
#include "gnc-engine.h"
|
#include "gnc-engine.h"
|
||||||
|
#include "policy.h"
|
||||||
|
|
||||||
/* ============================================================== */
|
/* ============================================================== */
|
||||||
/** The FIFOPolicy routines try to encapsulate the FIFO-specific
|
/** The Policy routines try to encapsulate the FIFO/LIFO-specific
|
||||||
* parts of the cap-gains routine, and can eb replaced by something
|
* parts of the cap-gains routine, and can be replaced by something
|
||||||
* else for other policies (e.g. LIFO)
|
* else for other policies.
|
||||||
*
|
*
|
||||||
* The FIFOPolicyGetLot() routine returns a lot into which the
|
* The PolicyGetLot() routine returns a lot into which the
|
||||||
* indicated split should be placed.
|
* indicated split should be placed.
|
||||||
*
|
*
|
||||||
* The FIFOPolicyGetSplit() routine returns an unassinged split
|
* The PolicyGetSplit() routine returns an unassinged split
|
||||||
* from the account that is appropriate for placing into the
|
* from the account that is appropriate for placing into the
|
||||||
* indicated lot. For the FIFO policy, that would be the
|
* indicated lot. For the FIFO policy, that would be the
|
||||||
* earliest split that is not in any account, and is of the
|
* earliest split that is not in any account, and is of the
|
||||||
* appropriate sign.
|
* appropriate sign. For a LIFO, it would be the latest.
|
||||||
*
|
*
|
||||||
* The FIFOPolicyIsOpeningSplit() predicate returns a true/false
|
* The PolicyIsOpeningSplit() predicate returns a true/false
|
||||||
* value, indicating if the indicated split was used to 'open'
|
* value, indicating if the indicated split was used to 'open'
|
||||||
* or 'grow' the lot.
|
* or 'grow' the lot.
|
||||||
*
|
*
|
||||||
* The FIFOPolicyGetLotOpening() routine returns information about
|
* The PolicyGetLotOpening() routine returns information about
|
||||||
* the opening balances for the lot. The 'opening balances'
|
* the opening balances for the lot. The 'opening balances'
|
||||||
* are the sum of all the splits used to grow (increase the size
|
* 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
|
* of) the lot. For a LIFO or FIFO policy, there is only one
|
||||||
* that opens a lot.
|
* split that opens a lot.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
GNCLot * FIFOPolicyGetLot (Split *split, gpointer user_data);
|
struct gncpolicy_s
|
||||||
|
{
|
||||||
|
GNCLot * (*PolicyGetLot) (GNCPolicy *, Split *split);
|
||||||
|
Split * (*PolicyGetSplit) (GNCPolicy *, GNCLot *lot);
|
||||||
|
void (*PolicyGetLotOpening) (GNCPolicy *, GNCLot *lot,
|
||||||
|
gnc_numeric *ret_amount,
|
||||||
|
gnc_numeric *ret_value,
|
||||||
|
gnc_commodity **ret_currency);
|
||||||
|
|
||||||
Split * FIFOPolicyGetSplit (GNCLot *lot, gpointer user_data);
|
gboolean (*PolicyIsOpeningSplit) (GNCPolicy *, GNCLot *lot,
|
||||||
|
Split *split);
|
||||||
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 */
|
#endif /* XACC_POLICY_P_H */
|
||||||
|
@ -48,14 +48,14 @@
|
|||||||
|
|
||||||
/* ============================================================== */
|
/* ============================================================== */
|
||||||
|
|
||||||
GNCLot *
|
static GNCLot *
|
||||||
FIFOPolicyGetLot (Split *split, gpointer user_data)
|
FIFOPolicyGetLot (GNCPolicy *pcy, Split *split)
|
||||||
{
|
{
|
||||||
return xaccAccountFindEarliestOpenLot (split->acc, split->amount);
|
return xaccAccountFindEarliestOpenLot (split->acc, split->amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
Split *
|
static Split *
|
||||||
FIFOPolicyGetSplit (GNCLot *lot, gpointer user_data)
|
FIFOPolicyGetSplit (GNCPolicy *pcy, GNCLot *lot)
|
||||||
{
|
{
|
||||||
SplitList *node;
|
SplitList *node;
|
||||||
gboolean want_positive;
|
gboolean want_positive;
|
||||||
@ -77,11 +77,11 @@ FIFOPolicyGetSplit (GNCLot *lot, gpointer user_data)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static void
|
||||||
FIFOPolicyGetLotOpening (GNCLot *lot,
|
FIFOPolicyGetLotOpening (GNCPolicy *pcy,
|
||||||
|
GNCLot *lot,
|
||||||
gnc_numeric *ret_amount, gnc_numeric *ret_value,
|
gnc_numeric *ret_amount, gnc_numeric *ret_value,
|
||||||
gnc_commodity **ret_currency,
|
gnc_commodity **ret_currency)
|
||||||
gpointer user_data)
|
|
||||||
{
|
{
|
||||||
Split *opening_split;
|
Split *opening_split;
|
||||||
opening_split = gnc_lot_get_earliest_split(lot);
|
opening_split = gnc_lot_get_earliest_split(lot);
|
||||||
@ -91,13 +91,116 @@ FIFOPolicyGetLotOpening (GNCLot *lot,
|
|||||||
if (ret_currency) *ret_currency = opening_split->parent->common_currency;
|
if (ret_currency) *ret_currency = opening_split->parent->common_currency;
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean
|
static gboolean
|
||||||
FIFOPolicyIsOpeningSplit (GNCLot *lot, Split *split, gpointer user_data)
|
FIFOPolicyIsOpeningSplit (GNCPolicy *pcy, GNCLot *lot, Split *split)
|
||||||
{
|
{
|
||||||
Split *opening_split;
|
Split *opening_split;
|
||||||
opening_split = gnc_lot_get_earliest_split(lot);
|
opening_split = gnc_lot_get_earliest_split(lot);
|
||||||
return (split == opening_split);
|
return (split == opening_split);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ============================================================== */
|
||||||
|
/* Define a single, static policy, since we have no per-object data.
|
||||||
|
* I suppose this could change, but we don't need any better at the
|
||||||
|
* moment ... */
|
||||||
|
|
||||||
|
GNCPolicy *
|
||||||
|
xaccGetFIFOPolicy (void)
|
||||||
|
{
|
||||||
|
static GNCPolicy *pcy = NULL;
|
||||||
|
|
||||||
|
if (!pcy)
|
||||||
|
{
|
||||||
|
pcy = g_new (GNCPolicy, 1);
|
||||||
|
pcy->PolicyGetLot = FIFOPolicyGetLot;
|
||||||
|
pcy->PolicyGetSplit = FIFOPolicyGetSplit;
|
||||||
|
pcy->PolicyGetLotOpening = FIFOPolicyGetLotOpening;
|
||||||
|
pcy->PolicyIsOpeningSplit = FIFOPolicyIsOpeningSplit;
|
||||||
|
}
|
||||||
|
return pcy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================== */
|
||||||
|
/* Stab at implementing the LIFO policy. This is untested.
|
||||||
|
* I'm not sure I got it right.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static GNCLot *
|
||||||
|
LIFOPolicyGetLot (GNCPolicy *pcy, Split *split)
|
||||||
|
{
|
||||||
|
return xaccAccountFindLatestOpenLot (split->acc, split->amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Split *
|
||||||
|
LIFOPolicyGetSplit (GNCPolicy *pcy, GNCLot *lot)
|
||||||
|
{
|
||||||
|
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 latest,
|
||||||
|
* we merely start at the end and go backwards. */
|
||||||
|
node = xaccAccountGetSplitList (lot->account);
|
||||||
|
node = g_list_last (node);
|
||||||
|
for (; node; node=node->prev)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This routine is actually identical to FIFO... */
|
||||||
|
static void
|
||||||
|
LIFOPolicyGetLotOpening (GNCPolicy *pcy,
|
||||||
|
GNCLot *lot,
|
||||||
|
gnc_numeric *ret_amount, gnc_numeric *ret_value,
|
||||||
|
gnc_commodity **ret_currency)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This routine is actually identical to FIFO... */
|
||||||
|
static gboolean
|
||||||
|
LIFOPolicyIsOpeningSplit (GNCPolicy *pcy, GNCLot *lot, Split *split)
|
||||||
|
{
|
||||||
|
Split *opening_split;
|
||||||
|
opening_split = gnc_lot_get_earliest_split(lot);
|
||||||
|
return (split == opening_split);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================== */
|
||||||
|
|
||||||
|
/* Define a single, static policy, since we have no per-object data.
|
||||||
|
* I suppose this could change, but we don't need any better at the
|
||||||
|
* moment ... */
|
||||||
|
|
||||||
|
GNCPolicy *
|
||||||
|
xaccGetLIFOPolicy (void)
|
||||||
|
{
|
||||||
|
static GNCPolicy *pcy = NULL;
|
||||||
|
|
||||||
|
if (!pcy)
|
||||||
|
{
|
||||||
|
pcy = g_new (GNCPolicy, 1);
|
||||||
|
pcy->PolicyGetLot = LIFOPolicyGetLot;
|
||||||
|
pcy->PolicyGetSplit = LIFOPolicyGetSplit;
|
||||||
|
pcy->PolicyGetLotOpening = LIFOPolicyGetLotOpening;
|
||||||
|
pcy->PolicyIsOpeningSplit = LIFOPolicyIsOpeningSplit;
|
||||||
|
}
|
||||||
|
return pcy;
|
||||||
|
}
|
||||||
|
|
||||||
/* =========================== END OF FILE ======================= */
|
/* =========================== END OF FILE ======================= */
|
||||||
|
@ -24,22 +24,18 @@
|
|||||||
* @author Created by Linas Vepstas August 2003
|
* @author Created by Linas Vepstas August 2003
|
||||||
* @author Copyright (c) 2003 Linas Vepstas <linas@linas.org>
|
* @author Copyright (c) 2003 Linas Vepstas <linas@linas.org>
|
||||||
*
|
*
|
||||||
* This file implements Accounting Policy. The Accounting Polciy
|
* This file implements Accounting Policy. The Accounting Policy
|
||||||
* determines how splits are assigned to lots. The default policy
|
* determines how splits are assigned to lots. The default policy
|
||||||
* is teh FIFO policy: the first thing bought is also the first
|
* is the FIFO policy: the first thing bought is also the first
|
||||||
* thing sold.
|
* thing sold.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef XACC_POLICY_H
|
#ifndef XACC_POLICY_H
|
||||||
#define XACC_POLICY_H
|
#define XACC_POLICY_H
|
||||||
|
|
||||||
#include "gnc-engine.h"
|
typedef struct gncpolicy_s GNCPolicy;
|
||||||
|
|
||||||
|
GNCPolicy *xaccGetFIFOPolicy (void);
|
||||||
|
GNCPolicy *xaccGetLIFOPolicy (void);
|
||||||
|
|
||||||
/* 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 */
|
#endif /* XACC_POLICY_H */
|
||||||
|
Loading…
Reference in New Issue
Block a user