policy.c

Go to the documentation of this file.
00001 /********************************************************************\
00002  * policy.c -- Implement FIFO Accounting Policy                     *
00003  *                                                                  *
00004  * This program is free software; you can redistribute it and/or    *
00005  * modify it under the terms of the GNU General Public License as   *
00006  * published by the Free Software Foundation; either version 2 of   *
00007  * the License, or (at your option) any later version.              *
00008  *                                                                  *
00009  * This program is distributed in the hope that it will be useful,  *
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
00012  * GNU General Public License for more details.                     *
00013  *                                                                  *
00014  * You should have received a copy of the GNU General Public License*
00015  * along with this program; if not, contact:                        *
00016  *                                                                  *
00017  * Free Software Foundation           Voice:  +1-617-542-5942       *
00018  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
00019  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
00020 \********************************************************************/
00021 
00032 #include "config.h"
00033 
00034 #include <glib.h>
00035 
00036 #include "Account.h"
00037 #include "Transaction.h"
00038 #include "TransactionP.h"
00039 #include "cap-gains.h"
00040 #include "gnc-engine.h"
00041 #include "gnc-lot.h"
00042 #include "gnc-lot-p.h"
00043 #include "policy.h"
00044 #include "policy-p.h"
00045 
00046 //static QofLogModule log_module = GNC_MOD_LOT;
00047 
00048 static Split * 
00049 DirectionPolicyGetSplit (GNCPolicy *pcy, GNCLot *lot, short reverse)
00050 {
00051    Split *split;
00052    SplitList *node;
00053    gnc_commodity *common_currency;
00054    gboolean want_positive;
00055    gnc_numeric baln;
00056    Split *osplit;
00057    Transaction *otrans;
00058    Timespec open_ts;
00059 
00060    if (!pcy || !lot || !lot->account || !lot->splits) return NULL;
00061 
00062    /* Recomputing the balance re-evaluates the lot closure */
00063    baln = gnc_lot_get_balance (lot);
00064    if (gnc_lot_is_closed(lot)) return NULL;
00065 
00066    want_positive = gnc_numeric_negative_p (baln);
00067 
00068    /* All splits in lot must share a common transaction currency. */
00069    split = lot->splits->data;
00070    common_currency = split->parent->common_currency;
00071    
00072    /* Don't add a split to the lot unless it will be the new last
00073       split in the lot.  Otherwise our balance tests will be wrong
00074       and the lot may end up too thin or too fat. */
00075    osplit = gnc_lot_get_latest_split (lot);
00076    otrans = osplit ? xaccSplitGetParent (osplit) : 0;
00077    open_ts = xaccTransRetDatePostedTS (otrans);
00078 
00079    /* Walk over *all* splits in the account, till we find one that
00080     * hasn't been assigned to a lot.  Return that split.
00081     * Make use of the fact that the splits in an account are 
00082     * already in date order; so we don't have to sort. */
00083    node = xaccAccountGetSplitList (lot->account);
00084    if (reverse)
00085    {
00086        node = g_list_last (node);
00087    }
00088    while (node)
00089    {
00090       gboolean is_match;
00091       gboolean is_positive;
00092       Timespec this_ts;
00093       split = node->data;
00094       if (split->lot) goto donext;
00095 
00096       /* Skip it if it's too early */
00097       this_ts = xaccTransRetDatePostedTS ( xaccSplitGetParent (split));
00098       if ((this_ts.tv_sec < open_ts.tv_sec) ||
00099           ((this_ts.tv_sec == open_ts.tv_sec) && 
00100             (this_ts.tv_nsec < open_ts.tv_nsec)))
00101       {
00102          if (reverse)
00103             /* Going backwards, no point in looking further */
00104             break;
00105          goto donext;
00106       }
00107       
00108       /* Allow equiv currencies */
00109       is_match = gnc_commodity_equiv (common_currency, 
00110                                       split->parent->common_currency);
00111       if (FALSE == is_match) goto donext;
00112 
00113       /* Disallow zero-amount splits in general. */
00114       if (gnc_numeric_zero_p(split->amount)) goto donext;
00115 
00116       is_positive = gnc_numeric_positive_p (split->amount);
00117       if ((want_positive && is_positive) ||
00118           ((!want_positive) && (!is_positive))) return split;
00119 donext:
00120       if (reverse)
00121       {
00122          node=node->prev;
00123       }
00124       else
00125       {
00126          node=node->next;
00127       }
00128    }
00129    return NULL;
00130 }
00131 
00132 /* ============================================================== */
00133 
00134 static GNCLot * 
00135 FIFOPolicyGetLot (GNCPolicy *pcy, Split *split)
00136 {
00137    if (!split) return NULL;
00138    return xaccAccountFindEarliestOpenLot (split->acc, split->amount,
00139                                           split->parent->common_currency);
00140 }
00141 
00142 static Split * 
00143 FIFOPolicyGetSplit (GNCPolicy *pcy, GNCLot *lot)
00144 {
00145    return DirectionPolicyGetSplit (pcy, lot, 0);
00146 }
00147 
00148 static void
00149 FIFOPolicyGetLotOpening (GNCPolicy *pcy,
00150         GNCLot *lot,
00151         gnc_numeric *ret_amount, gnc_numeric *ret_value,
00152         gnc_commodity **ret_currency)
00153 {
00154    Split *opening_split;
00155    opening_split = gnc_lot_get_earliest_split(lot);
00156 
00157    if (ret_amount) *ret_amount = opening_split->amount;
00158    if (ret_value) *ret_value = opening_split->value;
00159    if (ret_currency) *ret_currency = opening_split->parent->common_currency;
00160 }
00161 
00162 static gboolean
00163 FIFOPolicyIsOpeningSplit (GNCPolicy *pcy, GNCLot *lot, Split *split)
00164 {
00165    Split *opening_split;
00166    opening_split = gnc_lot_get_earliest_split(lot);
00167    return (split == opening_split);
00168 }
00169 
00170 /* ============================================================== */
00171 /* Define a single, static policy, since we have no per-object data.
00172  * I suppose this could change, but we don't need any better at the
00173  * moment ... */
00174 
00175 GNCPolicy *
00176 xaccGetFIFOPolicy (void)
00177 {
00178    static GNCPolicy *pcy = NULL;
00179 
00180    if (!pcy)
00181    {
00182       pcy = g_new (GNCPolicy, 1);
00183       pcy->PolicyGetLot = FIFOPolicyGetLot;
00184       pcy->PolicyGetSplit = FIFOPolicyGetSplit;
00185       pcy->PolicyGetLotOpening = FIFOPolicyGetLotOpening;
00186       pcy->PolicyIsOpeningSplit = FIFOPolicyIsOpeningSplit;
00187    }
00188    return pcy;
00189 }
00190 
00191 /* ============================================================== */
00192 /* Stab at implementing the LIFO policy.  This is untested. 
00193  * I'm not sure I got it right.
00194  */
00195 
00196 static GNCLot * 
00197 LIFOPolicyGetLot (GNCPolicy *pcy, Split *split)
00198 {
00199    if (!split) return NULL;
00200    return xaccAccountFindLatestOpenLot (split->acc, split->amount,
00201                                         split->parent->common_currency);
00202 }
00203 
00204 static Split * 
00205 LIFOPolicyGetSplit (GNCPolicy *pcy, GNCLot *lot)
00206 {
00207    return DirectionPolicyGetSplit (pcy, lot, 1);
00208 }
00209 
00210 /* This routine is actually identical to FIFO... */
00211 static void
00212 LIFOPolicyGetLotOpening (GNCPolicy *pcy,
00213         GNCLot *lot,
00214         gnc_numeric *ret_amount, gnc_numeric *ret_value,
00215         gnc_commodity **ret_currency)
00216 {
00217    Split *opening_split;
00218    opening_split = gnc_lot_get_earliest_split(lot);
00219 
00220    if (ret_amount) *ret_amount = opening_split->amount;
00221    if (ret_value) *ret_value = opening_split->value;
00222    if (ret_currency) *ret_currency = opening_split->parent->common_currency;
00223 }
00224 
00225 /* This routine is actually identical to FIFO... */
00226 static gboolean
00227 LIFOPolicyIsOpeningSplit (GNCPolicy *pcy, GNCLot *lot, Split *split)
00228 {
00229    Split *opening_split;
00230    opening_split = gnc_lot_get_earliest_split(lot);
00231    return (split == opening_split);
00232 }
00233 
00234 /* ============================================================== */
00235 
00236 /* Define a single, static policy, since we have no per-object data.
00237  * I suppose this could change, but we don't need any better at the
00238  * moment ... */
00239 
00240 GNCPolicy *
00241 xaccGetLIFOPolicy (void)
00242 {
00243    static GNCPolicy *pcy = NULL;
00244 
00245    if (!pcy)
00246    {
00247       pcy = g_new (GNCPolicy, 1);
00248       pcy->PolicyGetLot = LIFOPolicyGetLot;
00249       pcy->PolicyGetSplit = LIFOPolicyGetSplit;
00250       pcy->PolicyGetLotOpening = LIFOPolicyGetLotOpening;
00251       pcy->PolicyIsOpeningSplit = LIFOPolicyIsOpeningSplit;
00252    }
00253    return pcy;
00254 }
00255 
00256 /* =========================== END OF FILE ======================= */

Generated on Fri Oct 10 05:06:35 2008 for GnuCash by  doxygen 1.5.2