|
GnuCash 2.4.99
|
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 ======================= */
1.7.4