GnuCash  4.11-354-g0815ab64c1
policy.c
Go to the documentation of this file.
1 /********************************************************************\
2  * policy.c -- Implement FIFO Accounting Policy *
3  * *
4  * This program is free software; you can redistribute it and/or *
5  * modify it under the terms of the GNU General Public License as *
6  * published by the Free Software Foundation; either version 2 of *
7  * the License, or (at your option) any later version. *
8  * *
9  * This program is distributed in the hope that it will be useful, *
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12  * GNU General Public License for more details. *
13  * *
14  * You should have received a copy of the GNU General Public License*
15  * along with this program; if not, contact: *
16  * *
17  * Free Software Foundation Voice: +1-617-542-5942 *
18  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
19  * Boston, MA 02110-1301, USA gnu@gnu.org *
20 \********************************************************************/
21 
32 #include <config.h>
33 
34 #include <glib.h>
35 
36 #include "Account.h"
37 #include "Transaction.h"
38 #include "TransactionP.h"
39 #include "cap-gains.h"
40 #include "gnc-engine.h"
41 #include "gnc-lot.h"
42 #include "policy.h"
43 #include "policy-p.h"
44 
45 #ifndef SWIG /* swig doesn't see N_() as a string constant */
46 #include <glib/gi18n.h>
47 #else
48 #define N_(string) string
49 #endif
50 
51 #define FIFO_POLICY "fifo"
52 #define FIFO_POLICY_DESC N_("First In, First Out")
53 #define FIFO_POLICY_HINT N_("Use oldest lots first.")
54 #define LIFO_POLICY "lifo"
55 #define LIFO_POLICY_DESC N_("Last In, First Out")
56 #define LIFO_POLICY_HINT N_("Use newest lots first.")
57 #define AVERAGE_POLICY "average"
58 #define AVERAGE_POLICY_DESC N_("Average")
59 #define AVERAGE_POLICY_HINT N_("Average cost of open lots.")
60 #define MANUAL_POLICY "manual"
61 #define MANUAL_POLICY_DESC N_("Manual")
62 #define MANUAL_POLICY_HINT N_("Manually select lots.")
63 
64 //static QofLogModule log_module = GNC_MOD_LOT;
65 
66 static Split *
67 DirectionPolicyGetSplit (GNCPolicy *pcy, GNCLot *lot, short reverse)
68 {
69  Split *split;
70  SplitList *node;
71  gnc_commodity *common_currency;
72  gboolean want_positive;
73  gnc_numeric baln;
74  Split *osplit;
75  Transaction *otrans;
76  time64 open_time;
77  Account* lot_account;
78 
79  if (!pcy || !lot || !gnc_lot_get_split_list(lot)) return NULL;
80  lot_account = gnc_lot_get_account(lot);
81  if (!lot_account) return NULL;
82 
83  /* Recomputing the balance re-evaluates the lot closure */
84  baln = gnc_lot_get_balance (lot);
85  if (gnc_lot_is_closed(lot)) return NULL;
86 
87  want_positive = gnc_numeric_negative_p (baln);
88 
89  /* All splits in lot must share a common transaction currency. */
90  split = gnc_lot_get_split_list(lot)->data;
91  common_currency = split->parent->common_currency;
92 
93  /* Don't add a split to the lot unless it will be the new last
94  split in the lot. Otherwise our balance tests will be wrong
95  and the lot may end up too thin or too fat. */
96  osplit = gnc_lot_get_latest_split (lot);
97  otrans = osplit ? xaccSplitGetParent (osplit) : 0;
98  open_time = xaccTransRetDatePosted (otrans);
99 
100  /* Walk over *all* splits in the account, till we find one that
101  * hasn't been assigned to a lot. Return that split.
102  * Make use of the fact that the splits in an account are
103  * already in date order; so we don't have to sort. */
104  node = xaccAccountGetSplitList (lot_account);
105  if (reverse)
106  {
107  node = g_list_last (node);
108  }
109  while (node)
110  {
111  gboolean is_match;
112  gboolean is_positive;
113  time64 this_time;
114  split = node->data;
115  if (split->lot) goto donext;
116 
117  /* Skip it if it's too early */
118  this_time = xaccTransRetDatePosted ( xaccSplitGetParent (split));
119  if (this_time < open_time)
120  {
121  if (reverse)
122  /* Going backwards, no point in looking further */
123  break;
124  goto donext;
125  }
126 
127  /* Allow equiv currencies */
128  is_match = gnc_commodity_equiv (common_currency,
129  split->parent->common_currency);
130  if (FALSE == is_match) goto donext;
131 
132  /* Disallow zero-amount splits in general. */
133  if (gnc_numeric_zero_p(split->amount)) goto donext;
134 
135  is_positive = gnc_numeric_positive_p (split->amount);
136  if ((want_positive && is_positive) ||
137  ((!want_positive) && (!is_positive))) return split;
138 donext:
139  if (reverse)
140  {
141  node = node->prev;
142  }
143  else
144  {
145  node = node->next;
146  }
147  }
148  return NULL;
149 }
150 
151 /* ============================================================== */
152 
153 static GNCLot *
154 FIFOPolicyGetLot (GNCPolicy *pcy, Split *split)
155 {
156  if (!split) return NULL;
157  return xaccAccountFindEarliestOpenLot (split->acc, split->amount,
158  split->parent->common_currency);
159 }
160 
161 static Split *
162 FIFOPolicyGetSplit (GNCPolicy *pcy, GNCLot *lot)
163 {
164  return DirectionPolicyGetSplit (pcy, lot, 0);
165 }
166 
167 static void
168 FIFOPolicyGetLotOpening (GNCPolicy *pcy,
169  GNCLot *lot,
170  gnc_numeric *ret_amount, gnc_numeric *ret_value,
171  gnc_commodity **ret_currency)
172 {
173  Split *opening_split;
174  opening_split = gnc_lot_get_earliest_split(lot);
175 
176  if (ret_amount) *ret_amount = opening_split->amount;
177  if (ret_value) *ret_value = opening_split->value;
178  if (ret_currency) *ret_currency = opening_split->parent->common_currency;
179 }
180 
181 static gboolean
182 FIFOPolicyIsOpeningSplit (GNCPolicy *pcy, GNCLot *lot, Split *split)
183 {
184  Split *opening_split;
185  opening_split = gnc_lot_get_earliest_split(lot);
186  return (split == opening_split);
187 }
188 
189 GNCPolicy *
191 {
192  static GNCPolicy *pcy = NULL;
193 
194  if (!pcy)
195  {
196  pcy = g_new (GNCPolicy, 1);
197  pcy->PolicyGetLot = FIFOPolicyGetLot;
198  pcy->PolicyGetSplit = FIFOPolicyGetSplit;
199  pcy->PolicyGetLotOpening = FIFOPolicyGetLotOpening;
200  pcy->PolicyIsOpeningSplit = FIFOPolicyIsOpeningSplit;
201  }
202  return pcy;
203 }
204 
205 static GNCLot *
206 LIFOPolicyGetLot (GNCPolicy *pcy, Split *split)
207 {
208  if (!split) return NULL;
209  return xaccAccountFindLatestOpenLot (split->acc, split->amount,
210  split->parent->common_currency);
211 }
212 
213 static Split *
214 LIFOPolicyGetSplit (GNCPolicy *pcy, GNCLot *lot)
215 {
216  return DirectionPolicyGetSplit (pcy, lot, 1);
217 }
218 
219 /* This routine is actually identical to FIFO... */
220 static void
221 LIFOPolicyGetLotOpening (GNCPolicy *pcy,
222  GNCLot *lot,
223  gnc_numeric *ret_amount, gnc_numeric *ret_value,
224  gnc_commodity **ret_currency)
225 {
226  Split *opening_split;
227  opening_split = gnc_lot_get_earliest_split(lot);
228 
229  if (ret_amount) *ret_amount = opening_split->amount;
230  if (ret_value) *ret_value = opening_split->value;
231  if (ret_currency) *ret_currency = opening_split->parent->common_currency;
232 }
233 
234 /* This routine is actually identical to FIFO... */
235 static gboolean
236 LIFOPolicyIsOpeningSplit (GNCPolicy *pcy, GNCLot *lot, Split *split)
237 {
238  Split *opening_split;
239  opening_split = gnc_lot_get_earliest_split(lot);
240  return (split == opening_split);
241 }
242 
243 /* =========================== END OF FILE ======================= */
GNCLot * xaccAccountFindEarliestOpenLot(Account *acc, gnc_numeric sign, gnc_commodity *currency)
The xaccAccountFindEarliestOpenLot() method is a handy utility routine for finding the earliest open ...
Definition: cap-gains.c:190
SplitList * xaccAccountGetSplitList(const Account *acc)
The xaccAccountGetSplitList() routine returns a pointer to a GList of the splits in the account...
Definition: Account.cpp:4007
gboolean gnc_numeric_zero_p(gnc_numeric a)
Returns 1 if the given gnc_numeric is 0 (zero), else returns 0.
Transaction * xaccSplitGetParent(const Split *split)
Returns the parent transaction of the split.
Split * gnc_lot_get_earliest_split(GNCLot *lot)
The gnc_lot_get_earliest_split() routine is a convenience routine that helps identify the earliest da...
Definition: gnc-lot.c:701
gboolean gnc_numeric_negative_p(gnc_numeric a)
Returns 1 if a < 0, otherwise returns 0.
Split * gnc_lot_get_latest_split(GNCLot *lot)
The gnc_lot_get_latest_split() routine is a convenience routine that helps identify the date this lot...
Definition: gnc-lot.c:713
GList SplitList
GList of Split.
Definition: gnc-engine.h:211
Account handling public routines.
Implement Accounting Policy.
Implement Accounting Policy Private header File.
SplitList * gnc_lot_get_split_list(const GNCLot *lot)
The gnc_lot_get_split_list() routine returns a GList of all the splits in this lot.
Definition: gnc-lot.c:436
time64 xaccTransRetDatePosted(const Transaction *trans)
Retrieve the posted date of the transaction.
Definition: Transaction.c:2484
All type declarations for the whole Gnucash engine.
gboolean gnc_numeric_positive_p(gnc_numeric a)
Returns 1 if a > 0, otherwise returns 0.
gboolean gnc_lot_is_closed(GNCLot *lot)
The gnc_lot_is_closed() routine returns a boolean flag: is this lot closed? A lot is closed if its ba...
Definition: gnc-lot.c:382
gint64 time64
Many systems, including Microsoft Windows and BSD-derived Unixes like Darwin, are retaining the int-3...
Definition: gnc-date.h:93
Account * gnc_lot_get_account(const GNCLot *lot)
The gnc_lot_get_account() routine returns the account with which this lot is associated.
Definition: gnc-lot.c:392
API for Transactions and Splits (journal entries)
Utilities to Automatically Compute Capital Gains/Losses.
gboolean gnc_commodity_equiv(const gnc_commodity *a, const gnc_commodity *b)
This routine returns TRUE if the two commodities are equivalent.
gnc_numeric gnc_lot_get_balance(GNCLot *lot)
The gnc_lot_get_balance() routine returns the balance of the lot.
Definition: gnc-lot.c:530
GNCPolicy * xaccGetFIFOPolicy(void)
First-in, First-out Policy This policy will create FIFO Lots.
Definition: policy.c:190