GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
Files | Functions

Files

file  cap-gains.h
 Utilities to Automatically Compute Capital Gains/Losses.
 

Functions

gnc_numeric xaccSplitGetCapGains (Split *)
 
gboolean xaccAccountHasTrades (const Account *)
 
GNCLotxaccAccountFindEarliestOpenLot (Account *acc, gnc_numeric sign, gnc_commodity *currency)
 
GNCLotxaccAccountFindLatestOpenLot (Account *acc, gnc_numeric sign, gnc_commodity *currency)
 
SplitxaccSplitGetCapGainsSplit (const Split *)
 
SplitxaccSplitGetGainsSourceSplit (const Split *)
 
gboolean xaccSplitAssign (Split *split)
 
SplitxaccSplitAssignToLot (Split *split, GNCLot *lot)
 
void xaccSplitComputeCapGains (Split *split, Account *gain_acc)
 
void xaccLotComputeCapGains (GNCLot *lot, Account *gain_acc)
 

Detailed Description

This file implements the various routines to automatically compute and handle Cap Gains/Losses resulting from trading activities. Some of these routines might have broader applicability, for handling depreciation & etc.

This code is under development, and is 'beta': we think we're mostly done, and we've tested and "things work for us", but there may still be something missing, and there might still be some bugs.

This code does not currently handle tax distinctions, e.g the different tax treatment that short-term and long-term cap gains have.

The computation of (Realized) Gains/Losses is performed automatically by the lot "scrub" routines, using a "double-balance" algorithm. Every split has two numbers associated with it: an "amount", which is the number of items that a split describes, and the "value", which is the cost of those items. In a closed lot, the grand-total amount of items in the lot is zero: the number of items bought equals the number of items sold; thus the amount-balance is zero. But since the purchase/sale of the items in the lot typically happen at different prices, there will typically be a gain/loss. This gain/loss is the grand-total value of all the items in the lot (total costs minus total income).

In order to properly account for the gains/losses, an "adjusting split" is added that brings the total gains/losses back to exactly zero (this is the second "balance" of "double balance"). This adjusting split will have an amount of zero (no items are involved) but have a non-zero value (equal to the total gain/loss). This split can then participate in a "gains transaction" which records the gains in another account. Thus, for example, if you record $300 in your bank account due to the purchase and then the sale of some item, the "gains transaction" will record $300 in income in an income account. Thus, the change in the bank balance is always reflected by an equal change in income, assuring that the books are balanced.

Notes about auto-recompute: If the amount in a split is changed, then the lot has to be recomputed. This has a potential trickle-through effect on all later lots. Ideally, later lots are dissolved, and recomputed. However, some lots may have been user-hand-built. These should be left alone.

ToDo: o XXX Need to create a data-integrity scrubber, tht makes sure that the various flags, and pointers & etc. match.

Function Documentation

GNCLot* xaccAccountFindEarliestOpenLot ( Account acc,
gnc_numeric  sign,
gnc_commodity currency 
)

The xaccAccountFindEarliestOpenLot() method is a handy utility routine for finding the earliest open lot in an account whose lot balance is opposite to the passed argument 'sign'. By 'earliest lot', we mean the lot that has a split with the earliest 'date_posted'. The sign comparison helps identify a lot that can be added to: usually, one wants to add splits to a lot so that the balance only decreases. If 'currency' is non-null, then this attempts to find a lot whose opening transaction has the same currency.

Definition at line 191 of file cap-gains.c.

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 }
gchar * gnc_num_dbg_to_string(gnc_numeric n)
#define ENTER(format, args...)
Definition: qoflog.h:261
const char * gnc_lot_get_title(const GNCLot *lot)
Definition: gnc-lot.c:437
#define LEAVE(format, args...)
Definition: qoflog.h:271
gnc_numeric gnc_lot_get_balance(GNCLot *lot)
Definition: gnc-lot.c:477
gboolean xaccAccountHasTrades ( const Account )

The xaccAccountHasTrades() method checks to see if the indicated account is used in the trading of commodities. A 'trading' account will contain transactions whose transaction currency is not the same as the account commodity. The existance of such transactions is the very definition of a 'trade'. This routine returns TRUE if this is a trading account, else it returns FALSE.

Definition at line 79 of file cap-gains.c.

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 }
SplitList * xaccAccountGetSplitList(const Account *acc)
Definition: Account.c:3717
gboolean xaccAccountIsPriced(const Account *acc)
Definition: Account.c:4249
GList SplitList
Definition: gnc-engine.h:203
Definition: SplitP.h:71
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Definition: Account.c:3148
gboolean xaccSplitAssign ( Split split)

The`xaccSplitAssign() routine will take the indicated split and, if it doesn't already belong to a lot, it will attempt to assign it to an appropriate lot. If the split already belongs to a Lot, this routine does nothing. If there are no open Lots, this routine will create a new lot and place the split into it. If there's an open lot, and its big enough to accept the split in it's entirety, then the split will be placed into that lot. If the split is too big to fit into the currently open lot, it will be busted up into two (or more) pieces, and each placed into a lot accordingly. If the split needed to be broken up into several pieces, this routine will return TRUE, else it returns FALSE.

If the split had to be broken up, kvp markup in the "/lot-split" directory is used to identify the peers. 'gemini'-style kvp's are used.

This routine uses the "FIFOPolicy" callback, and thus implements a "FIFO" First-In First-Out accounting policy. This is currently the only implemented policy; adding new policies should be 'easy'; read the source luke.

Definition at line 438 of file cap-gains.c.

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 }
gchar * gnc_num_dbg_to_string(gnc_numeric n)
#define PINFO(format, args...)
Definition: qoflog.h:249
gboolean xaccAccountHasTrades(const Account *acc)
Definition: cap-gains.c:79
gboolean gnc_numeric_zero_p(gnc_numeric a)
#define ENTER(format, args...)
Definition: qoflog.h:261
const char * gnc_lot_get_title(const GNCLot *lot)
Definition: gnc-lot.c:437
Split * xaccSplitAssignToLot(Split *split, GNCLot *lot)
Definition: cap-gains.c:222
GNCLot * gnc_lot_make_default(Account *acc)
Definition: gnc-lot.c:755
void xaccAccountBeginEdit(Account *acc)
Definition: Account.c:1280
#define LEAVE(format, args...)
Definition: qoflog.h:271
void xaccAccountCommitEdit(Account *acc)
Definition: Account.c:1321
GNCPolicy * gnc_account_get_policy(Account *acc)
Definition: Account.c:1880
Split* xaccSplitAssignToLot ( Split split,
GNCLot lot 
)

The xaccSplitAssignToLot() routine will fit the indicated split into the indicated lot, with the goal of closing the lot, or at least bringing the lot balance closer to closure. (A closed lot has a balance of zero). To make this "fit", a variety of checks and actions are performed. First, the lot must be open, and the sign of the split amount must be opposite to the sign of the lot balance. The 'opposite-sign' requirement is so that inserting the split will cause the size of the lot to decrease. If the amount of the split is too small, or is just right to close the lot, the split is added, and NULL is returned. If the split is larger than the lot balance, the split will be divided into sub-splits, one of which is just right to close the lot. A pointer to the other sub-split will be returned.

If the split had to be broken up, kvp markup in the "/lot-split" directory is used to identify the peers. 'gemini'-style kvp's are used.

Definition at line 222 of file cap-gains.c.

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 }
void xaccSplitSetValue(Split *s, gnc_numeric amt)
Definition: Split.c:1294
#define xaccTransAppendSplit(t, s)
Definition: Transaction.h:357
gchar * gnc_num_dbg_to_string(gnc_numeric n)
QofBook * qof_instance_get_book(gconstpointer)
#define PINFO(format, args...)
Definition: qoflog.h:249
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
void gnc_lot_add_split(GNCLot *lot, Split *split)
Definition: gnc-lot.c:569
Use a 64-bit unsigned int timespec.
Definition: gnc-date.h:299
gboolean gnc_numeric_zero_p(gnc_numeric a)
void xaccSplitSetReconcile(Split *split, char recn)
Definition: Split.c:1826
gint gnc_numeric_compare(gnc_numeric a, gnc_numeric b)
#define PERR(format, args...)
Definition: qoflog.h:237
const char * gnc_lot_get_title(const GNCLot *lot)
Definition: gnc-lot.c:437
#define PWARN(format, args...)
Definition: qoflog.h:243
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
const char * xaccTransGetDescription(const Transaction *trans)
Definition: Transaction.c:2184
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
gboolean gnc_numeric_positive_p(gnc_numeric a)
Split * xaccMallocSplit(QofBook *book)
Definition: Split.c:582
gboolean xaccTransGetVoidStatus(const Transaction *trans)
Definition: Transaction.c:2526
gboolean gnc_lot_is_closed(GNCLot *lot)
Definition: gnc-lot.c:376
Definition: SplitP.h:71
void xaccAccountBeginEdit(Account *acc)
Definition: Account.c:1280
#define xaccAccountInsertSplit(acc, s)
Definition: Account.h:972
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
const char * xaccAccountGetName(const Account *acc)
Definition: Account.c:3031
#define GNC_DENOM_AUTO
Definition: gnc-numeric.h:246
void xaccAccountCommitEdit(Account *acc)
Definition: Account.c:1321
gnc_numeric gnc_lot_get_balance(GNCLot *lot)
Definition: gnc-lot.c:477
void xaccSplitComputeCapGains ( Split split,
Account gain_acc 
)

The xaccSplitComputeCapGains() routine computes the cap gains or losses for the indicated split. The gains are placed into the 'gains_acct'. If the gains_acct is NULL, then the appropriate default account is used (and created, if needed).

To compute the gains, the split must belong to a lot. If the split is the 'opening split', i.e. the earliest split in the lot, then nothing is done, as there are no gains/losses (something must be bought and sold for there to be a gain/loss).

Note also: the 'amount' of the split must be of opposite sign, and must be equal to or smaller, than the 'amount' of the opening split; its an error otherwise. If the 'amount' of the split is less than the opening amount, the gains are pro-rated.

The xaccLotComputeCapGains() routine merely invokes the above on each split in the lot.

Definition at line 536 of file cap-gains.c.

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 }
void xaccSplitSetValue(Split *s, gnc_numeric amt)
Definition: Split.c:1294
#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)
QofBook * qof_instance_get_book(gconstpointer)
#define PINFO(format, args...)
Definition: qoflog.h:249
gnc_numeric gnc_numeric_neg(gnc_numeric a)
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.
gboolean gnc_commodity_equal(const gnc_commodity *a, const gnc_commodity *b)
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
Use a 64-bit unsigned int timespec.
Definition: gnc-date.h:299
gboolean gnc_numeric_zero_p(gnc_numeric a)
Transaction * xaccSplitGetParent(const Split *split)
Definition: Split.c:1903
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
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
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
gboolean gnc_numeric_positive_p(gnc_numeric a)
Split * xaccMallocSplit(QofBook *book)
Definition: Split.c:582
gnc_numeric gnc_numeric_sub(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
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
time64 gnc_time(time64 *tbuf)
get the current local time
GNCNumericErrorCode gnc_numeric_check(gnc_numeric a)
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
void xaccAccountCommitEdit(Account *acc)
Definition: Account.c:1321
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
void xaccTransSetDatePostedTS(Transaction *trans, const Timespec *ts)
Definition: Transaction.c:1970
gnc_numeric xaccSplitGetAmount(const Split *split)
Definition: Split.c:1987
gnc_numeric xaccSplitGetCapGains ( Split )

The xaccSplitGetCapGains() method returns the value of capital gains (if any) associated with the indicated split. In order for there to be any capital gains, several things must hold true about this split: (1) It must have been involved in trading (for aexample, by belonging to a stock or trading account) (2) It must have been assigned to a lot. (3) It cannot be the opening split of a lot; that is, it must be a matching sale of an earlier purchase (or vice versa).

Definition at line 911 of file cap-gains.c.

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 }
void xaccSplitComputeCapGains(Split *split, Account *gain_acc)
Definition: cap-gains.c:536
#define ENTER(format, args...)
Definition: qoflog.h:261
#define LEAVE(format, args...)
Definition: qoflog.h:271
Split* xaccSplitGetCapGainsSplit ( const Split )

The xaccSplitGetCapGainsSplit() routine returns the split that records the cap gains for this split. It returns NULL if not found. This routine does nothing more than search for the split recorded in the KVP key "/gains-split"

Definition at line 492 of file cap-gains.c.

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 }
void qof_instance_get(const QofInstance *inst, const gchar *first_param,...)
Wrapper for g_object_get.
QofInstance * qof_collection_lookup_entity(const QofCollection *, const GncGUID *)
#define PINFO(format, args...)
Definition: qoflog.h:249
QofCollection * qof_instance_get_collection(gconstpointer inst)
Definition: guid.h:65
Definition: SplitP.h:71
Split* xaccSplitGetGainsSourceSplit ( const Split )

The xaccSplitGetGainsSourceSplit() routine returns the split that is the source of the cap gains in this split. It returns NULL if not found. This routine does nothing more than search for the split recorded in the KVP key "/gains-source"

Definition at line 514 of file cap-gains.c.

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 }
void qof_instance_get(const QofInstance *inst, const gchar *first_param,...)
Wrapper for g_object_get.
QofInstance * qof_collection_lookup_entity(const QofCollection *, const GncGUID *)
#define PINFO(format, args...)
Definition: qoflog.h:249
QofCollection * qof_instance_get_collection(gconstpointer inst)
Definition: guid.h:65
Definition: SplitP.h:71