GnuCash 2.4.99
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 "policy.h"
00043 #include "policy-p.h"
00044 
00045 //static QofLogModule log_module = GNC_MOD_LOT;
00046 
00047 static Split *
00048 DirectionPolicyGetSplit (GNCPolicy *pcy, GNCLot *lot, short reverse)
00049 {
00050     Split *split;
00051     SplitList *node;
00052     gnc_commodity *common_currency;
00053     gboolean want_positive;
00054     gnc_numeric baln;
00055     Split *osplit;
00056     Transaction *otrans;
00057     Timespec open_ts;
00058     Account* lot_account;
00059 
00060     if (!pcy || !lot || !gnc_lot_get_split_list(lot)) return NULL;
00061     lot_account = gnc_lot_get_account(lot);
00062     if (!lot_account) return NULL;
00063 
00064     /* Recomputing the balance re-evaluates the lot closure */
00065     baln = gnc_lot_get_balance (lot);
00066     if (gnc_lot_is_closed(lot)) return NULL;
00067 
00068     want_positive = gnc_numeric_negative_p (baln);
00069 
00070     /* All splits in lot must share a common transaction currency. */
00071     split = gnc_lot_get_split_list(lot)->data;
00072     common_currency = split->parent->common_currency;
00073 
00074     /* Don't add a split to the lot unless it will be the new last
00075        split in the lot.  Otherwise our balance tests will be wrong
00076        and the lot may end up too thin or too fat. */
00077     osplit = gnc_lot_get_latest_split (lot);
00078     otrans = osplit ? xaccSplitGetParent (osplit) : 0;
00079     open_ts = xaccTransRetDatePostedTS (otrans);
00080 
00081     /* Walk over *all* splits in the account, till we find one that
00082      * hasn't been assigned to a lot.  Return that split.
00083      * Make use of the fact that the splits in an account are
00084      * already in date order; so we don't have to sort. */
00085     node = xaccAccountGetSplitList (lot_account);
00086     if (reverse)
00087     {
00088         node = g_list_last (node);
00089     }
00090     while (node)
00091     {
00092         gboolean is_match;
00093         gboolean is_positive;
00094         Timespec this_ts;
00095         split = node->data;
00096         if (split->lot) goto donext;
00097 
00098         /* Skip it if it's too early */
00099         this_ts = xaccTransRetDatePostedTS ( xaccSplitGetParent (split));
00100         if ((this_ts.tv_sec < open_ts.tv_sec) ||
00101                 ((this_ts.tv_sec == open_ts.tv_sec) &&
00102                  (this_ts.tv_nsec < open_ts.tv_nsec)))
00103         {
00104             if (reverse)
00105                 /* Going backwards, no point in looking further */
00106                 break;
00107             goto donext;
00108         }
00109 
00110         /* Allow equiv currencies */
00111         is_match = gnc_commodity_equiv (common_currency,
00112                                         split->parent->common_currency);
00113         if (FALSE == is_match) goto donext;
00114 
00115         /* Disallow zero-amount splits in general. */
00116         if (gnc_numeric_zero_p(split->amount)) goto donext;
00117 
00118         is_positive = gnc_numeric_positive_p (split->amount);
00119         if ((want_positive && is_positive) ||
00120                 ((!want_positive) && (!is_positive))) return split;
00121 donext:
00122         if (reverse)
00123         {
00124             node = node->prev;
00125         }
00126         else
00127         {
00128             node = node->next;
00129         }
00130     }
00131     return NULL;
00132 }
00133 
00134 /* ============================================================== */
00135 
00136 static GNCLot *
00137 FIFOPolicyGetLot (GNCPolicy *pcy, Split *split)
00138 {
00139     if (!split) return NULL;
00140     return xaccAccountFindEarliestOpenLot (split->acc, split->amount,
00141                                            split->parent->common_currency);
00142 }
00143 
00144 static Split *
00145 FIFOPolicyGetSplit (GNCPolicy *pcy, GNCLot *lot)
00146 {
00147     return DirectionPolicyGetSplit (pcy, lot, 0);
00148 }
00149 
00150 static void
00151 FIFOPolicyGetLotOpening (GNCPolicy *pcy,
00152                          GNCLot *lot,
00153                          gnc_numeric *ret_amount, gnc_numeric *ret_value,
00154                          gnc_commodity **ret_currency)
00155 {
00156     Split *opening_split;
00157     opening_split = gnc_lot_get_earliest_split(lot);
00158 
00159     if (ret_amount) *ret_amount = opening_split->amount;
00160     if (ret_value) *ret_value = opening_split->value;
00161     if (ret_currency) *ret_currency = opening_split->parent->common_currency;
00162 }
00163 
00164 static gboolean
00165 FIFOPolicyIsOpeningSplit (GNCPolicy *pcy, GNCLot *lot, Split *split)
00166 {
00167     Split *opening_split;
00168     opening_split = gnc_lot_get_earliest_split(lot);
00169     return (split == opening_split);
00170 }
00171 
00172 /* ============================================================== */
00173 /* Define a single, static policy, since we have no per-object data.
00174  * I suppose this could change, but we don't need any better at the
00175  * moment ... */
00176 
00177 GNCPolicy *
00178 xaccGetFIFOPolicy (void)
00179 {
00180     static GNCPolicy *pcy = NULL;
00181 
00182     if (!pcy)
00183     {
00184         pcy = g_new (GNCPolicy, 1);
00185         pcy->PolicyGetLot = FIFOPolicyGetLot;
00186         pcy->PolicyGetSplit = FIFOPolicyGetSplit;
00187         pcy->PolicyGetLotOpening = FIFOPolicyGetLotOpening;
00188         pcy->PolicyIsOpeningSplit = FIFOPolicyIsOpeningSplit;
00189     }
00190     return pcy;
00191 }
00192 
00193 /* ============================================================== */
00194 /* Stab at implementing the LIFO policy.  This is untested.
00195  * I'm not sure I got it right.
00196  */
00197 
00198 static GNCLot *
00199 LIFOPolicyGetLot (GNCPolicy *pcy, Split *split)
00200 {
00201     if (!split) return NULL;
00202     return xaccAccountFindLatestOpenLot (split->acc, split->amount,
00203                                          split->parent->common_currency);
00204 }
00205 
00206 static Split *
00207 LIFOPolicyGetSplit (GNCPolicy *pcy, GNCLot *lot)
00208 {
00209     return DirectionPolicyGetSplit (pcy, lot, 1);
00210 }
00211 
00212 /* This routine is actually identical to FIFO... */
00213 static void
00214 LIFOPolicyGetLotOpening (GNCPolicy *pcy,
00215                          GNCLot *lot,
00216                          gnc_numeric *ret_amount, gnc_numeric *ret_value,
00217                          gnc_commodity **ret_currency)
00218 {
00219     Split *opening_split;
00220     opening_split = gnc_lot_get_earliest_split(lot);
00221 
00222     if (ret_amount) *ret_amount = opening_split->amount;
00223     if (ret_value) *ret_value = opening_split->value;
00224     if (ret_currency) *ret_currency = opening_split->parent->common_currency;
00225 }
00226 
00227 /* This routine is actually identical to FIFO... */
00228 static gboolean
00229 LIFOPolicyIsOpeningSplit (GNCPolicy *pcy, GNCLot *lot, Split *split)
00230 {
00231     Split *opening_split;
00232     opening_split = gnc_lot_get_earliest_split(lot);
00233     return (split == opening_split);
00234 }
00235 
00236 /* =========================== END OF FILE ======================= */
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines