GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
cap-gains.c
Go to the documentation of this file.
1 /********************************************************************\
2  * cap-gains.c -- Automatically Compute Capital Gains/Losses *
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 [email protected] *
20 \********************************************************************/
21 
56 #include "config.h"
57 
58 #include <glib.h>
59 #include <glib/gi18n.h>
60 
61 #include "AccountP.h"
62 #include "Scrub2.h"
63 #include "Scrub3.h"
64 #include "Transaction.h"
65 #include "TransactionP.h"
66 #include "cap-gains.h"
67 #include "gnc-engine.h"
68 #include "engine-helpers.h"
69 #include "gnc-lot.h"
70 #include "policy.h"
71 #include "policy-p.h"
72 
73 static QofLogModule log_module = GNC_MOD_LOT;
74 
75 
76 /* ============================================================== */
77 
78 gboolean
80 {
81  gnc_commodity *acc_comm;
82  SplitList *splits, *node;
83 
84  if (!acc) return FALSE;
85 
86  if (xaccAccountIsPriced (acc))
87  return TRUE;
88 
89  acc_comm = xaccAccountGetCommodity(acc);
90 
91  splits = xaccAccountGetSplitList(acc);
92  for (node = splits; node; node = node->next)
93  {
94  Split *s = node->data;
95  Transaction *t = s->parent;
96  if (s->gains == GAINS_STATUS_GAINS) continue;
97  if (acc_comm != t->common_currency) return TRUE;
98  }
99 
100  return FALSE;
101 }
102 
103 /* ============================================================== */
104 
106 {
107  GNCLot *lot;
108  gnc_commodity *currency;
109  Timespec ts;
110  int (*numeric_pred)(gnc_numeric);
111  gboolean (*date_pred)(Timespec e, Timespec tr);
112 };
113 
114 static gboolean
115 earliest_pred (Timespec earl, Timespec tran)
116 {
117  return ((earl.tv_sec > tran.tv_sec) ||
118  ((earl.tv_sec == tran.tv_sec) && (earl.tv_nsec > tran.tv_nsec)));
119 }
120 
121 static gboolean
122 latest_pred (Timespec earl, Timespec tran)
123 {
124  return ((earl.tv_sec < tran.tv_sec) ||
125  ((earl.tv_sec == tran.tv_sec) && (earl.tv_nsec < tran.tv_nsec)));
126 }
127 
128 static gpointer
129 finder_helper (GNCLot *lot, gpointer user_data)
130 {
131  struct find_lot_s *els = user_data;
132  Split *s;
133  Transaction *trans;
134  gnc_numeric bal;
135  gboolean opening_is_positive, bal_is_positive;
136 
137  if (gnc_lot_is_closed (lot)) return NULL;
138 
139  s = gnc_lot_get_earliest_split (lot);
140  if (s == NULL) return NULL;
141 
142  /* We want a lot whose balance is of the correct sign. All splits
143  in a lot must be the opposite sign of the opening split. We also
144  want to ignore lots that are overfull, i.e., where the balance in
145  the lot is of opposite sign to the opening split in the lot. */
146  if (0 == (els->numeric_pred) (s->amount)) return NULL;
147  bal = gnc_lot_get_balance (lot);
148  opening_is_positive = gnc_numeric_positive_p (s->amount);
149  bal_is_positive = gnc_numeric_positive_p (bal);
150  if (opening_is_positive != bal_is_positive) return NULL;
151 
152  trans = s->parent;
153  if (els->currency &&
154  (FALSE == gnc_commodity_equiv (els->currency,
155  trans->common_currency)))
156  {
157  return NULL;
158  }
159 
160  if (els->date_pred (els->ts, trans->date_posted))
161  {
162  els->ts = trans->date_posted;
163  els->lot = lot;
164  }
165 
166  return NULL;
167 }
168 
169 static inline GNCLot *
170 xaccAccountFindOpenLot (Account *acc, gnc_numeric sign,
171  gnc_commodity *currency,
172  gint64 guess,
173  gboolean (*date_pred)(Timespec, Timespec))
174 {
175  struct find_lot_s es;
176 
177  es.lot = NULL;
178  es.currency = currency;
179  es.ts.tv_sec = guess;
180  es.ts.tv_nsec = 0;
181  es.date_pred = date_pred;
182 
183  if (gnc_numeric_positive_p(sign)) es.numeric_pred = gnc_numeric_negative_p;
184  else es.numeric_pred = gnc_numeric_positive_p;
185 
186  xaccAccountForEachLot (acc, finder_helper, &es);
187  return es.lot;
188 }
189 
190 GNCLot *
192  gnc_commodity *currency)
193 {
194  GNCLot *lot;
195  ENTER (" sign=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT, sign.num,
196  sign.denom);
197 
198  lot = xaccAccountFindOpenLot (acc, sign, currency,
199  G_MAXINT64, earliest_pred);
200  LEAVE ("found lot=%p %s baln=%s", lot, gnc_lot_get_title (lot),
202  return lot;
203 }
204 
205 GNCLot *
206 xaccAccountFindLatestOpenLot (Account *acc, gnc_numeric sign,
207  gnc_commodity *currency)
208 {
209  GNCLot *lot;
210  ENTER (" sign=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT,
211  sign.num, sign.denom);
212 
213  lot = xaccAccountFindOpenLot (acc, sign, currency,
214  G_MININT64, latest_pred);
215  LEAVE ("found lot=%p %s", lot, gnc_lot_get_title (lot));
216  return lot;
217 }
218 
219 /* ============================================================== */
220 
221 Split *
223 {
224  Account *acc;
225  gnc_numeric baln;
226  int cmp;
227  gboolean baln_is_positive, amt_is_positive;
228 
229  if (!lot) return split;
230  if (!split) return NULL;
231 
232  /* If this split already belongs to a lot, we are done. */
233  if (split->lot) return NULL;
234 
235  /* Anomolous situation; except for voided transactions,
236  * we don't expect to see splits with no amount ..
237  * unless they're gains splits, and we shouldn't see those.
238  */
239  if (gnc_numeric_zero_p (split->amount))
240  {
241  if (xaccTransGetVoidStatus(split->parent)) return NULL;
242 
243  PWARN ("split with zero amount; value=%s gflag=%x gsplit=%p",
244  gnc_num_dbg_to_string (split->amount),
245  split->gains,
246  split->gains_split);
247  if (split->gains_split)
248  {
249  PWARN ("gains amt=%s value=%s",
250  gnc_num_dbg_to_string (split->gains_split->amount),
251  gnc_num_dbg_to_string (split->gains_split->value));
252  }
253  return NULL;
254  }
255 
256  /* If the lot is closed, we can't add anything to it */
257  baln = gnc_lot_get_balance (lot);
258  if (gnc_lot_is_closed (lot)) return split;
259 
260  /* If the lot balance is zero, but the lot is open, then
261  * the lot is empty. Unconditionally add the split. */
262  if (gnc_numeric_zero_p (baln))
263  {
264  acc = split->acc;
265  xaccAccountBeginEdit (acc);
266  gnc_lot_add_split (lot, split);
267  PINFO ("added split to empty lot, new lot baln=%s (%s)",
269  gnc_lot_get_title (lot));
270  xaccAccountCommitEdit (acc);
271  return NULL;
272  }
273 
274  /* If the sign of the split is the same as the sign of the lot,
275  * add the split, but complain about it ... none of the currently
276  * implemented accounting policies should be giving us splits
277  * that make lots larger. One a lot is open, the FIFO/LIFO
278  * policies should be working only to make the lot smaller.
279  * We can remove teh warning emssage come the day we have
280  * fancier policies.
281  */
282  baln_is_positive = gnc_numeric_positive_p (baln);
283  amt_is_positive = gnc_numeric_positive_p (split->amount);
284  if ((baln_is_positive && amt_is_positive) ||
285  ((!baln_is_positive) && (!amt_is_positive)))
286  {
287  PWARN ("accounting policy gave us split that enlarges the lot!\n"
288  "old lot baln=%s split amt=%s lot=%s",
290  gnc_num_dbg_to_string (split->amount),
291  gnc_lot_get_title (lot));
292 
293  acc = split->acc;
294  xaccAccountBeginEdit (acc);
295  gnc_lot_add_split (lot, split);
296  xaccAccountCommitEdit (acc);
297  return NULL;
298  }
299 
300  /* If adding the split would make the lot balance change sign,
301  * then we split the split into two pieces: one piece that will
302  * bring the lot balance to zero, and another to be dealt with
303  * later. */
304  cmp = gnc_numeric_compare (gnc_numeric_abs(split->amount),
305  gnc_numeric_abs(baln));
306 
307  PINFO ("found open lot with baln=%s (%s)", gnc_num_dbg_to_string (baln),
308  gnc_lot_get_title (lot));
309 
310  /* cmp == -1 if amt < baln, ==0 if amt==baln */
311  if (0 >= cmp)
312  {
313  acc = split->acc;
314  xaccAccountBeginEdit (acc);
315  gnc_lot_add_split (lot, split);
316  PINFO ("simple added split to lot, new lot baln=%s",
318  xaccAccountCommitEdit (acc);
319  return NULL;
320  }
321 
322  /* If we are here, then (cmp == +1 iff (amt > baln)) and we need
323  * to split up the split into pieces. Do it. */
324  {
325  time64 now = gnc_time (NULL);
326  Split * new_split;
327  gnc_numeric amt_a, amt_b, amt_tot;
328  gnc_numeric val_a, val_b, val_tot;
329  gnc_numeric frac;
330  Transaction *trans;
331  Timespec ts;
332 
333  acc = split->acc;
334  xaccAccountBeginEdit (acc);
335  trans = split->parent;
336  xaccTransBeginEdit (trans);
337 
338  amt_tot = split->amount;
339  amt_a = gnc_numeric_neg (baln);
340  amt_b = gnc_numeric_sub_fixed (amt_tot, amt_a);
341  g_assert (gnc_numeric_check(amt_b) == GNC_ERROR_OK);
342 
343  PINFO ("++++++++++++++ splitting split=%p into amt = %s + %s",
344  split,
345  gnc_num_dbg_to_string(amt_a),
346  gnc_num_dbg_to_string(amt_b) );
347 
348  /* Compute the value so that it holds in the same proportion:
349  * i.e. so that (amt_a / amt_tot) = (val_a / val_tot)
350  */
351  val_tot = split->value;
352  frac = gnc_numeric_div (amt_a, amt_tot,
354  val_a = gnc_numeric_mul (frac, val_tot,
355  gnc_numeric_denom(val_tot),
357 
358  val_b = gnc_numeric_sub_fixed (val_tot, val_a);
359  if (gnc_numeric_check(val_a))
360  {
361  PERR("Numeric overflow\n"
362  "Acct=%s Txn=%s\n"
363  "\tval_tot=%s amt_a=%s amt_tot=%s\n",
364  xaccAccountGetName(acc),
366  gnc_num_dbg_to_string(val_tot),
367  gnc_num_dbg_to_string(amt_a),
368  gnc_num_dbg_to_string(amt_tot));
369  }
370 
371  if (gnc_numeric_zero_p(val_a) || gnc_numeric_zero_p(val_b))
372  {
373  PERR ("Failed to split into two!");
374  }
375 
376  PINFO ("split value is = %s = %s + %s",
377  gnc_num_dbg_to_string(val_tot),
378  gnc_num_dbg_to_string(val_a),
379  gnc_num_dbg_to_string(val_b) );
380 
381  g_assert (!gnc_numeric_zero_p (amt_a));
382  g_assert (!gnc_numeric_zero_p (val_a));
383  xaccSplitSetAmount (split, amt_a);
384  xaccSplitSetValue (split, val_a);
385 
386  /* Adding this split will have the effect of closing this lot,
387  * because the new balance should be precisely zero. */
388  gnc_lot_add_split (lot, split);
389 
390  /* Put the remainder of the balance into a new split,
391  * which is in other respects just a clone of this one. */
392  new_split = xaccMallocSplit (qof_instance_get_book(acc));
393 
394  /* Copy most of the split attributes */
395  xaccSplitSetMemo (new_split, xaccSplitGetMemo (split));
396  /* Set split-action with gnc_set_num_action which is the same as
397  * xaccSplitSetAction with these arguments; use gnc_get_num_action to get
398  * split-action which is the same as xaccSplitGetAction */
399  gnc_set_num_action(NULL, new_split, NULL, gnc_get_num_action(NULL, split));
400  xaccSplitSetReconcile (new_split, xaccSplitGetReconcile (split));
401  ts = xaccSplitRetDateReconciledTS (split);
402  xaccSplitSetDateReconciledTS (new_split, &ts);
403 
404  /* Set the lot-split and peer_guid properties on the two
405  * splits to indicate that they're linked.
406  */
407  qof_instance_set (QOF_INSTANCE (split),
408  "lot-split", now,
409  "peer_guid", xaccSplitGetGUID (new_split),
410  NULL);
411 
412  qof_instance_set (QOF_INSTANCE (new_split),
413  "lot-split", now,
414  "peer_guid", xaccSplitGetGUID (split),
415  NULL);
416 
417  xaccAccountInsertSplit (acc, new_split);
418  xaccTransAppendSplit (trans, new_split);
419  /* Set the amount and value after the split is in the transaction
420  so it can find the correct denominator to use. Otherwise it
421  uses 100000 which may cause an overflow in some of the tests
422  in test-period */
423  xaccSplitSetAmount (new_split, amt_b);
424  xaccSplitSetValue (new_split, val_b);
425  xaccTransCommitEdit (trans);
426  xaccAccountCommitEdit (acc);
427  return new_split;
428  }
429 }
430 
431 /* ============================================================== */
432 
433 /* Accounting-policy callback. Given an account and an amount,
434  * this routine should return a lot. By implementing this as
435  * a callback, we can 'easily' add other accounting policies.
436  */
437 gboolean
439 {
440  Account *acc;
441  gboolean splits_split_up = FALSE;
442  GNCLot *lot;
443  GNCPolicy *pcy;
444 
445  if (!split) return FALSE;
446 
447  /* If this split already belongs to a lot or the account doesn't
448  * have lots, we are done.
449  */
450  if (split->lot) return FALSE;
451  g_assert (split->gains == GAINS_STATUS_UNKNOWN ||
452  (split->gains & GAINS_STATUS_GAINS) == FALSE);
453  acc = split->acc;
454  if (!xaccAccountHasTrades (acc))
455  return FALSE;
456  if (gnc_numeric_zero_p (split->amount))
457  return FALSE;
458 
459  ENTER ("(split=%p)", split);
460 
461  pcy = gnc_account_get_policy(acc);
462  xaccAccountBeginEdit (acc);
463 
464  /* If we are here, this split does not belong to any lot.
465  * We ask the policy for a lot to assign it to. This
466  * block is written in the form of a while loop, since we
467  * may have to bust a split across several lots.
468  */
469  while (split)
470  {
471  PINFO ("have split %p amount=%s", split,
472  gnc_num_dbg_to_string (split->amount));
473  split->gains |= GAINS_STATUS_VDIRTY;
474  lot = pcy->PolicyGetLot (pcy, split);
475  if (!lot)
476  {
477  lot = gnc_lot_make_default (acc);
478  PINFO ("start new lot (%s)", gnc_lot_get_title(lot));
479  }
480  split = xaccSplitAssignToLot (split, lot);
481  if (split) splits_split_up = TRUE;
482  }
483  xaccAccountCommitEdit (acc);
484 
485  LEAVE (" split_up=%d", splits_split_up);
486  return splits_split_up;
487 }
488 
489 /* ============================================================== */
490 
491 Split *
493 {
494  GncGUID *gains_guid;
495  Split *gains_split;
496 
497  if (!split) return NULL;
498 
499  qof_instance_get (QOF_INSTANCE (split),
500  "gains-split", &gains_guid,
501  NULL);
502  if (!gains_guid) return NULL;
503 
504  /* Both splits will be in the same collection, so search there. */
505  gains_split = (Split*) qof_collection_lookup_entity (
506  qof_instance_get_collection(split), gains_guid);
507  PINFO ("split=%p has gains-split=%p", split, gains_split);
508  return gains_split;
509 }
510 
511 /* ============================================================== */
512 
513 Split *
515 {
516  GncGUID *source_guid;
517  Split *source_split;
518 
519  if (!split) return NULL;
520 
521  qof_instance_get (QOF_INSTANCE (split),
522  "gains-source", &source_guid,
523  NULL);
524  if (!source_guid) return NULL;
525 
526  /* Both splits will be in the same collection, so search there. */
527  source_split = (Split*) qof_collection_lookup_entity(
528  qof_instance_get_collection(split), source_guid);
529  PINFO ("split=%p has source-split=%p", split, source_split);
530  return source_split;
531 }
532 
533 /* ============================================================== */
534 
535 void
537 {
538  SplitList *node;
539  GNCLot *lot;
540  GNCPolicy *pcy;
541  gnc_commodity *currency = NULL;
542  gnc_numeric zero = gnc_numeric_zero();
543  gnc_numeric value = zero;
544  gnc_numeric frac;
545  gnc_numeric opening_amount, opening_value;
546  gnc_numeric lot_amount, lot_value;
547  gnc_commodity *opening_currency;
548 
549  if (!split) return;
550  lot = split->lot;
551  if (!lot) return;
553  currency = split->parent->common_currency;
554 
555  ENTER ("(split=%p gains=%p status=0x%x lot=%s)", split,
556  split->gains_split, split->gains, gnc_lot_get_title(lot));
557 
558  /* Make sure the status flags and pointers are initialized */
559  xaccSplitDetermineGainStatus(split);
560 
561  /* Not possible to have gains if the transaction currency and
562  * account commodity are identical. */
563  if (gnc_commodity_equal (currency,
564  xaccAccountGetCommodity(split->acc)))
565  {
566  LEAVE ("Currency transfer, gains not possible, returning.");
567  return;
568  }
569 
570  if (pcy->PolicyIsOpeningSplit (pcy, lot, split))
571  {
572 #if MOVE_THIS_TO_A_DATA_INTEGRITY_SCRUBBER
573  /* Check to make sure that this opening split doesn't
574  * have a cap-gain transaction associated with it.
575  * If it does, that's wrong, and we ruthlessly destroy it.
576  * XXX Don't do this, it leads to infinite loops.
577  * We need to scrub out errors like this elsewhere!
578  */
579  if (xaccSplitGetCapGainsSplit (split))
580  {
581  Split *gains_split = xaccSplitGetCapGainsSplit(split);
582  Transaction *trans = gains_split->parent;
583  PERR ("Opening Split must not have cap gains!!\n");
584 
585  xaccTransBeginEdit (trans);
586  xaccTransDestroy (trans);
587  xaccTransCommitEdit (trans);
588  }
589 #endif
590  LEAVE ("Lot opening split, returning.");
591  return;
592  }
593 
594  if (g_strcmp0 ("stock-split", xaccSplitGetType (split)) == 0)
595  {
596  LEAVE ("Stock split split, returning.");
597  return;
598  }
599 
600  if (GAINS_STATUS_GAINS & split->gains)
601  {
602  Split *s;
603  PINFO ("split is a gains recording split, switch over");
604  /* If this is the split that records the gains, then work with
605  * the split that generates the gains.
606  */
607  /* split = xaccSplitGetCapGainsSplit (split); */
608  s = split->gains_split;
609 
610  /* This should never be NULL, and if it is, and its matching
611  * parent can't be found, then its a bug, and we should be
612  * discarding this split. But ... for now .. return.
613  * XXX move appropriate actions to a 'scrub' routine'
614  */
615  if (!s)
616  {
617  PERR ("Bad gains-split pointer! .. trying to recover.");
618  split->gains_split = xaccSplitGetCapGainsSplit (split);
619  s = split->gains_split;
620  if (!s) return;
621 #if MOVE_THIS_TO_A_DATA_INTEGRITY_SCRUBBER
622  xaccTransDestroy (trans);
623 #endif
624  }
625  split = s;
626  }
627 
628  /* Note: if the value of the 'opening' split(s) has changed,
629  * then the cap gains are changed. So we need to check not
630  * only if this split is dirty, but also the lot-opening splits. */
631  for (node = gnc_lot_get_split_list(lot); node; node = node->next)
632  {
633  Split *s = node->data;
634  if (pcy->PolicyIsOpeningSplit(pcy, lot, s))
635  {
636  if (GAINS_STATUS_UNKNOWN == s->gains) xaccSplitDetermineGainStatus (s);
637  if (s->gains & GAINS_STATUS_VDIRTY)
638  {
639  /* Force a recompute to occur */
640  split->gains |= GAINS_STATUS_VDIRTY;
641  break;
642  }
643  }
644  }
645 
646  /* If it doesn't look like this split is 'dirty', then there's
647  * nothing to do. Just return. */
648  if ((FALSE == (split->gains & GAINS_STATUS_A_VDIRTY)) &&
649  (split->gains_split) &&
650  (FALSE == (split->gains_split->gains & GAINS_STATUS_A_VDIRTY)))
651  {
652  LEAVE ("split not dirty, returning");
653  return;
654  }
655 
656  /* Yow! If amount is zero, there's nothing to do! Amount-zero splits
657  * may exist if users attempted to manually record gains. */
658  if (gnc_numeric_zero_p (split->amount)) return;
659 
660  /* If we got to here, then the split or something related is
661  * 'dirty' and the gains really do need to be recomputed.
662  * So start working things. */
663 
664  /* Get the amount and value in this lot at the time of this transaction. */
665  gnc_lot_get_balance_before (lot, split, &lot_amount, &lot_value);
666 
667  pcy->PolicyGetLotOpening (pcy, lot, &opening_amount, &opening_value,
668  &opening_currency);
669 
670  /* Check to make sure the lot-opening currency and this split
671  * use the same currency */
672  if (FALSE == gnc_commodity_equiv (currency, opening_currency))
673  {
674  /* OK, the purchase and the sale were made in different currencies.
675  * I don't know how to compute cap gains for that. This is not
676  * an error. Just punt, silently.
677  */
678  LEAVE ("Can't compute gains, mismatched commodities!");
679  return;
680  }
681 
682  /* Opening amount should be larger (or equal) to current split,
683  * and it should be of the opposite sign.
684  * XXX This should really be a part of a scrub routine that
685  * cleans up the lot, before we get at it!
686  */
687  if (0 > gnc_numeric_compare (gnc_numeric_abs(lot_amount),
688  gnc_numeric_abs(split->amount)))
689  {
690  GList *n;
691  for (n = gnc_lot_get_split_list(lot); n; n = n->next)
692  {
693  Split *s = n->data;
694  PINFO ("split amt=%s", gnc_num_dbg_to_string(s->amount));
695  }
696  PERR ("Malformed Lot \"%s\"! (too thin!) "
697  "opening amt=%s split amt=%s baln=%s",
698  gnc_lot_get_title (lot),
699  gnc_num_dbg_to_string (lot_amount),
700  gnc_num_dbg_to_string (split->amount),
702  return;
703  }
704  if ( (gnc_numeric_negative_p(lot_amount) ||
705  gnc_numeric_positive_p(split->amount)) &&
706  (gnc_numeric_positive_p(lot_amount) ||
707  gnc_numeric_negative_p(split->amount)))
708  {
709  GList *n;
710  for (n = gnc_lot_get_split_list(lot); n; n = n->next)
711  {
712  Split *s = n->data;
713  PINFO ("split amt=%s", gnc_num_dbg_to_string(s->amount));
714  }
715  PERR ("Malformed Lot \"%s\"! (too fat!) "
716  "opening amt=%s split amt=%s baln=%s",
717  gnc_lot_get_title (lot),
718  gnc_num_dbg_to_string (lot_amount),
719  gnc_num_dbg_to_string (split->amount),
721  return;
722  }
723 
724  /* The cap gains is the difference between the basis prior to the
725  * current split, and the current split, pro-rated for an equal
726  * amount of shares.
727  * i.e. purchase_price = lot_value / lot_amount
728  * cost_basis = purchase_price * current_split_amount
729  * cap_gain = current_split_value - cost_basis
730  */
731  /* Fraction of the lot that this split represents: */
732  frac = gnc_numeric_div (split->amount, lot_amount,
735  /* Basis for this split: */
736  value = gnc_numeric_mul (frac, lot_value,
737  gnc_numeric_denom(opening_value),
739  /* Capital gain for this split: */
740  value = gnc_numeric_sub (value, split->value,
742  PINFO ("Open amt=%s val=%s; split amt=%s val=%s; gains=%s\n",
743  gnc_num_dbg_to_string (lot_amount),
744  gnc_num_dbg_to_string (lot_value),
745  gnc_num_dbg_to_string (split->amount),
746  gnc_num_dbg_to_string (split->value),
747  gnc_num_dbg_to_string (value));
748  if (gnc_numeric_check (value))
749  {
750  PERR ("Numeric overflow during gains calculation\n"
751  "Acct=%s Txn=%s\n"
752  "\tOpen amt=%s val=%s\n\tsplit amt=%s val=%s\n\tgains=%s\n",
753  xaccAccountGetName(split->acc),
754  xaccTransGetDescription(split->parent),
755  gnc_num_dbg_to_string (lot_amount),
756  gnc_num_dbg_to_string (lot_value),
757  gnc_num_dbg_to_string (split->amount),
758  gnc_num_dbg_to_string (split->value),
759  gnc_num_dbg_to_string (value));
760  return;
761  }
762 
763  /* Are the cap gains zero? If not, add a balancing transaction.
764  * As per design doc lots.txt: the transaction has two splits,
765  * with equal & opposite values. The amt of one iz zero (so as
766  * not to upset the lot balance), the amt of the other is the same
767  * as its value (its the realized gain/loss).
768  */
769  if (FALSE == gnc_numeric_zero_p (value))
770  {
771  Transaction *trans;
772  Split *lot_split, *gain_split;
773  Timespec ts;
774  gboolean new_gain_split;
775  gnc_numeric negvalue = gnc_numeric_neg (value);
776 
777  /* See if there already is an associated gains transaction.
778  * If there is, adjust its value as appropriate. Else, create
779  * a new gains transaction.
780  */
781  /* lot_split = xaccSplitGetCapGainsSplit (split); */
782  lot_split = split->gains_split;
783 
784  if (NULL == lot_split)
785  {
786  Account *lot_acc = gnc_lot_get_account(lot);
787  QofBook *book = qof_instance_get_book(lot_acc);
788  Transaction *base_txn = xaccSplitGetParent (split);
789 
790  new_gain_split = TRUE;
791 
792  lot_split = xaccMallocSplit (book);
793  gain_split = xaccMallocSplit (book);
794 
795  /* Check to make sure the gains account currency matches. */
796  if ((NULL == gain_acc) ||
797  (FALSE == gnc_commodity_equiv (currency,
798  xaccAccountGetCommodity(gain_acc))))
799  {
800  gain_acc = xaccAccountGainsAccount (lot_acc, currency);
801  }
802 
803  xaccAccountBeginEdit (gain_acc);
804  xaccAccountInsertSplit (gain_acc, gain_split);
805  xaccAccountCommitEdit (gain_acc);
806 
807  xaccAccountBeginEdit (lot_acc);
808  xaccAccountInsertSplit (lot_acc, lot_split);
809  xaccAccountCommitEdit (lot_acc);
810 
811  trans = xaccMallocTransaction (book);
812 
813  xaccTransBeginEdit (trans);
814  xaccTransSetCurrency (trans, currency);
815  xaccTransSetDescription (trans, _("Realized Gain/Loss"));
816 
817  xaccTransAppendSplit (trans, lot_split);
818  xaccTransAppendSplit (trans, gain_split);
819 
820  xaccSplitSetMemo (lot_split, _("Realized Gain/Loss"));
821  xaccSplitSetMemo (gain_split, _("Realized Gain/Loss"));
822 
823  /* For the new transaction, set the split properties indicating
824  * that this is the gains transaction that corresponds
825  * to the gains source.
826  */
827  xaccTransBeginEdit (base_txn);
828  qof_instance_set (QOF_INSTANCE (split),
829  "gains-split", xaccSplitGetGUID (lot_split),
830  NULL);
831  xaccTransCommitEdit (base_txn);
832  qof_instance_set (QOF_INSTANCE (lot_split),
833  "gains-source", xaccSplitGetGUID (split),
834  NULL);
835 
836  }
837  else
838  {
839  trans = lot_split->parent;
840  gain_split = xaccSplitGetOtherSplit (lot_split);
841 
842  /* If the gains transaction has been edited so that it no longer has
843  just two splits, ignore it and assume it's still correct. */
844  if (!gain_split)
845  {
846  new_gain_split = FALSE;
847  }
848  /* If the gain is already recorded corectly do nothing. This is
849  * more than just an optimization since this may be called during
850  * gnc_book_partition_txn and depending on the order in which things
851  * happen some splits may be in the wrong book at that time. */
852  else if (split->gains_split == lot_split &&
853  lot_split->gains_split == split &&
854  gain_split->gains_split == split &&
855  gnc_numeric_equal (xaccSplitGetValue (lot_split), value) &&
856  gnc_numeric_zero_p (xaccSplitGetAmount (lot_split)) &&
857  gnc_numeric_equal (xaccSplitGetValue (gain_split), negvalue) &&
858  gnc_numeric_equal (xaccSplitGetAmount (gain_split), negvalue))
859  {
860  new_gain_split = FALSE;
861  }
862  else
863  {
864  new_gain_split = TRUE;
865  xaccTransBeginEdit (trans);
866 
867  /* Make sure the existing gains trans has the correct currency,
868  * just in case someone screwed with it! */
869  if (FALSE == gnc_commodity_equiv(currency, trans->common_currency))
870  {
871  PWARN ("Resetting the transaction currency!");
872  xaccTransSetCurrency (trans, currency);
873  }
874  }
875  }
876 
877  if (new_gain_split)
878  {
879  /* Common to both */
880  ts = xaccTransRetDatePostedTS (split->parent);
881  xaccTransSetDatePostedTS (trans, &ts);
882  xaccTransSetDateEnteredSecs (trans, gnc_time (NULL));
883 
884  xaccSplitSetAmount (lot_split, zero);
885  xaccSplitSetValue (lot_split, value);
886 
887  xaccSplitSetAmount (gain_split, negvalue);
888  xaccSplitSetValue (gain_split, negvalue);
889 
890  /* Some short-cuts to help avoid the above property lookup. */
891  split->gains = GAINS_STATUS_CLEAN;
892  split->gains_split = lot_split;
893  lot_split->gains = GAINS_STATUS_GAINS;
894  lot_split->gains_split = split;
895  gain_split->gains = GAINS_STATUS_GAINS;
896  gain_split->gains_split = split;
897 
898  /* Do this last since it may generate an event that will call us
899  recursively. */
900  gnc_lot_add_split (lot, lot_split);
901 
902  xaccTransCommitEdit (trans);
903  }
904  }
905  LEAVE ("(lot=%s)", gnc_lot_get_title(lot));
906 }
907 
908 /* ============================================================== */
909 
912 {
913  if (!split) return gnc_numeric_zero();
914  ENTER("(split=%p)", split);
915 
916  if (GAINS_STATUS_UNKNOWN == split->gains)
917  xaccSplitDetermineGainStatus(split);
918  if ((split->gains & GAINS_STATUS_A_VDIRTY) ||
919  (split->gains_split &&
920  (split->gains_split->gains & GAINS_STATUS_A_VDIRTY)))
921  {
922  xaccSplitComputeCapGains (split, NULL);
923  }
924 
925  /* If this is the source split, get the gains from the one
926  * that records the gains. If this already is the gains split,
927  * its a no-op. */
928  if (!(GAINS_STATUS_GAINS & split->gains))
929  {
930  /* split = xaccSplitGetCapGainsSplit (split); */
931  split = split->gains_split;
932  }
933 
934  LEAVE("(split=%p)", split);
935  if (!split) return gnc_numeric_zero();
936 
937  return split->value;
938 }
939 
940 /* ============================================================== */
941 
942 void
943 xaccLotComputeCapGains (GNCLot *lot, Account *gain_acc)
944 {
945  SplitList *node;
946  GNCPolicy *pcy;
947  gboolean is_dirty = FALSE;
948 
949  /* Note: if the value of the 'opening' split(s) has changed,
950  * then the cap gains are changed. To capture this, we need
951  * to mark all splits dirty if the opening splits are dirty. */
952 
953  ENTER("(lot=%p)", lot);
955  for (node = gnc_lot_get_split_list(lot); node; node = node->next)
956  {
957  Split *s = node->data;
958  if (pcy->PolicyIsOpeningSplit(pcy, lot, s))
959  {
960  if (GAINS_STATUS_UNKNOWN == s->gains)
961  xaccSplitDetermineGainStatus(s);
962  if (s->gains & GAINS_STATUS_VDIRTY)
963  {
964  is_dirty = TRUE;
965  s->gains &= ~GAINS_STATUS_VDIRTY;
966  }
967  }
968  }
969 
970  if (is_dirty)
971  {
972  for (node = gnc_lot_get_split_list(lot); node; node = node->next)
973  {
974  Split *s = node->data;
975  s->gains |= GAINS_STATUS_VDIRTY;
976  }
977  }
978 
979  for (node = gnc_lot_get_split_list(lot); node; node = node->next)
980  {
981  Split *s = node->data;
982  xaccSplitComputeCapGains (s, gain_acc);
983  }
984  LEAVE("(lot=%p)", lot);
985 }
986 
987 /* =========================== END OF FILE ======================= */
void xaccSplitSetValue(Split *s, gnc_numeric amt)
Definition: Split.c:1294
GNCLot * xaccAccountFindEarliestOpenLot(Account *acc, gnc_numeric sign, gnc_commodity *currency)
Definition: cap-gains.c:191
High-Level API for imposing Lot constraints.
#define xaccTransAppendSplit(t, s)
Definition: Transaction.h:357
Transaction * xaccMallocTransaction(QofBook *book)
Definition: Transaction.c:513
gboolean gnc_numeric_equal(gnc_numeric a, gnc_numeric b)
gchar * gnc_num_dbg_to_string(gnc_numeric n)
void qof_instance_get(const QofInstance *inst, const gchar *first_param,...)
Wrapper for g_object_get.
gpointer xaccAccountForEachLot(const Account *acc, gpointer(*proc)(GNCLot *lot, gpointer user_data), gpointer user_data)
SplitList * xaccAccountGetSplitList(const Account *acc)
Definition: Account.c:3717
QofBook * qof_instance_get_book(gconstpointer)
gboolean xaccAccountIsPriced(const Account *acc)
Definition: Account.c:4249
QofInstance * qof_collection_lookup_entity(const QofCollection *, const GncGUID *)
#define PINFO(format, args...)
Definition: qoflog.h:249
Utilities to Convert Stock Accounts to use Lots.
gnc_numeric gnc_numeric_neg(gnc_numeric a)
Timespec xaccSplitRetDateReconciledTS(const Split *split)
Definition: Split.c:1884
void qof_instance_set(QofInstance *inst, const gchar *first_param,...)
Wrapper for g_object_set Group setting multiple parameters in a single begin/commit/rollback.
char xaccSplitGetReconcile(const Split *split)
Definition: Split.c:1980
gboolean gnc_commodity_equal(const gnc_commodity *a, const gnc_commodity *b)
void xaccSplitComputeCapGains(Split *split, Account *gain_acc)
Definition: cap-gains.c:536
void gnc_lot_add_split(GNCLot *lot, Split *split)
Definition: gnc-lot.c:569
void xaccTransSetDescription(Transaction *trans, const char *desc)
Definition: Transaction.c:2085
gboolean xaccAccountHasTrades(const Account *acc)
Definition: cap-gains.c:79
Use a 64-bit unsigned int timespec.
Definition: gnc-date.h:299
gboolean gnc_numeric_zero_p(gnc_numeric a)
Split * xaccSplitGetGainsSourceSplit(const Split *split)
Definition: cap-gains.c:514
void xaccSplitSetReconcile(Split *split, char recn)
Definition: Split.c:1826
Transaction * xaccSplitGetParent(const Split *split)
Definition: Split.c:1903
QofCollection * qof_instance_get_collection(gconstpointer inst)
gnc_numeric xaccSplitGetCapGains(Split *split)
Definition: cap-gains.c:911
gint gnc_numeric_compare(gnc_numeric a, gnc_numeric b)
#define PERR(format, args...)
Definition: qoflog.h:237
#define ENTER(format, args...)
Definition: qoflog.h:261
Split * xaccSplitGetCapGainsSplit(const Split *split)
Definition: cap-gains.c:492
Split * gnc_lot_get_earliest_split(GNCLot *lot)
Definition: gnc-lot.c:648
Definition: guid.h:65
gboolean gnc_numeric_negative_p(gnc_numeric a)
void xaccTransSetCurrency(Transaction *trans, gnc_commodity *curr)
Definition: Transaction.c:1354
const char * gnc_lot_get_title(const GNCLot *lot)
Definition: gnc-lot.c:437
void xaccTransDestroy(Transaction *trans)
Definition: Transaction.c:1402
#define PWARN(format, args...)
Definition: qoflog.h:243
GList SplitList
Definition: gnc-engine.h:203
void xaccSplitSetAmount(Split *s, gnc_numeric amt)
Definition: Split.c:1258
gnc_numeric gnc_numeric_mul(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
void xaccSplitSetMemo(Split *split, const char *memo)
Definition: Split.c:1774
Implement Accounting Policy.
Implement Accounting Policy Private header File.
SplitList * gnc_lot_get_split_list(const GNCLot *lot)
Definition: gnc-lot.c:417
const char * xaccTransGetDescription(const Transaction *trans)
Definition: Transaction.c:2184
void gnc_lot_get_balance_before(const GNCLot *lot, const Split *split, gnc_numeric *amount, gnc_numeric *value)
Definition: gnc-lot.c:519
gnc_numeric gnc_numeric_abs(gnc_numeric a)
void xaccTransCommitEdit(Transaction *trans)
Definition: Transaction.c:1579
gnc_numeric gnc_numeric_div(gnc_numeric x, gnc_numeric y, gint64 denom, gint how)
#define xaccSplitGetGUID(X)
Definition: Split.h:521
void xaccTransBeginEdit(Transaction *trans)
Definition: Transaction.c:1380
All type declarations for the whole Gnucash engine.
gboolean gnc_numeric_positive_p(gnc_numeric a)
struct _gnc_numeric gnc_numeric
An rational-number type.
Definition: gnc-numeric.h:68
Split * xaccMallocSplit(QofBook *book)
Definition: Split.c:582
gnc_numeric gnc_numeric_sub(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Split * xaccSplitAssignToLot(Split *split, GNCLot *lot)
Definition: cap-gains.c:222
gboolean xaccTransGetVoidStatus(const Transaction *trans)
Definition: Transaction.c:2526
GNCLot * gnc_lot_make_default(Account *acc)
Definition: gnc-lot.c:755
gboolean xaccSplitAssign(Split *split)
Definition: cap-gains.c:438
gboolean gnc_lot_is_closed(GNCLot *lot)
Definition: gnc-lot.c:376
Definition: SplitP.h:71
gnc_numeric xaccSplitGetValue(const Split *split)
Definition: Split.c:1993
void xaccAccountBeginEdit(Account *acc)
Definition: Account.c:1280
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Definition: Account.c:3148
#define xaccAccountInsertSplit(acc, s)
Definition: Account.h:972
Timespec xaccTransRetDatePostedTS(const Transaction *trans)
Definition: Transaction.c:2243
Split * xaccSplitGetOtherSplit(const Split *split)
Definition: Split.c:2086
#define LEAVE(format, args...)
Definition: qoflog.h:271
Account * xaccAccountGainsAccount(Account *acc, gnc_commodity *curr)
Definition: Account.c:4537
void xaccSplitSetDateReconciledTS(Split *split, Timespec *ts)
Definition: Split.c:1865
time64 gnc_time(time64 *tbuf)
get the current local time
GNCNumericErrorCode gnc_numeric_check(gnc_numeric a)
const char * xaccSplitGetMemo(const Split *split)
Definition: Split.c:1968
gint64 time64
Definition: gnc-date.h:83
Account * gnc_lot_get_account(const GNCLot *lot)
Definition: gnc-lot.c:386
void xaccTransSetDateEnteredSecs(Transaction *trans, time64 secs)
Definition: Transaction.c:1951
const char * xaccSplitGetType(const Split *s)
Definition: Split.c:2048
const char * xaccAccountGetName(const Account *acc)
Definition: Account.c:3031
#define GNC_DENOM_AUTO
Definition: gnc-numeric.h:246
API for Transactions and Splits (journal entries)
void xaccAccountCommitEdit(Account *acc)
Definition: Account.c:1321
Utilities to Automatically Compute Capital Gains/Losses.
GNCPolicy * gnc_account_get_policy(Account *acc)
Definition: Account.c:1880
gboolean gnc_commodity_equiv(const gnc_commodity *a, const gnc_commodity *b)
gnc_numeric gnc_lot_get_balance(GNCLot *lot)
Definition: gnc-lot.c:477
const gchar * QofLogModule
Definition: qofid.h:89
void xaccTransSetDatePostedTS(Transaction *trans, const Timespec *ts)
Definition: Transaction.c:1970
gnc_numeric xaccSplitGetAmount(const Split *split)
Definition: Split.c:1987