GnuCash  2.6.16
Files | Functions

Files

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

Functions

gnc_numeric xaccSplitGetCapGains (Split *)
 
gboolean xaccAccountHasTrades (const Account *)
 
GNCLot * xaccAccountFindEarliestOpenLot (Account *acc, gnc_numeric sign, gnc_commodity *currency)
 
GNCLot * xaccAccountFindLatestOpenLot (Account *acc, gnc_numeric sign, gnc_commodity *currency)
 
AccountxaccAccountGetDefaultGainAccount (const Account *acc, const gnc_commodity *currency)
 
void xaccAccountSetDefaultGainAccount (Account *acc, const Account *gains_acct)
 
Split * xaccSplitGetCapGainsSplit (const Split *)
 
Split * xaccSplitGetGainsSourceSplit (const Split *)
 
gboolean xaccSplitAssign (Split *split)
 
Split * xaccSplitAssignToLot (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

◆ xaccAccountFindEarliestOpenLot()

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 190 of file cap-gains.c.

192 {
193  GNCLot *lot;
194  ENTER (" sign=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT, sign.num,
195  sign.denom);
196 
197  lot = xaccAccountFindOpenLot (acc, sign, currency,
198  G_MAXINT64, earliest_pred);
199  LEAVE ("found lot=%p %s baln=%s", lot, gnc_lot_get_title (lot),
201  return lot;
202 }
gchar * gnc_num_dbg_to_string(gnc_numeric n)
Definition: gnc-numeric.c:1383
#define ENTER(format, args...)
Definition: qoflog.h:256
const char * gnc_lot_get_title(const GNCLot *lot)
Definition: gnc-lot.c:368
#define LEAVE(format, args...)
Definition: qoflog.h:266
gnc_numeric gnc_lot_get_balance(GNCLot *lot)
Definition: gnc-lot.c:404

◆ xaccAccountGetDefaultGainAccount()

Account* xaccAccountGetDefaultGainAccount ( const Account acc,
const gnc_commodity *  currency 
)

The xaccAccountGetDefaultGainAccount() routine will return the account to which realized gains/losses may be posted. Because gains may be in different currencies, one must specify the currency type in which the gains will be posted. This routine does nothing more than return the value of the "/lot-mgmt/gains-act/XXX" key, where XXX is the unique currency name. IOf there is no default account for this currency, NULL will be returned.

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

296 {
297  Account *gain_acct = NULL;
298  KvpFrame *cwd;
299  KvpValue *vvv;
300  GncGUID * gain_acct_guid;
301  const char * cur_name;
302 
303  if (!acc || !currency) return NULL;
304 
305  cwd = xaccAccountGetSlots (acc);
306  cwd = kvp_frame_get_frame_slash (cwd, "/lot-mgmt/gains-act/");
307 
308  /* Accounts are indexed by thier unique currency name */
309  cur_name = gnc_commodity_get_unique_name (currency);
310  vvv = kvp_frame_get_slot (cwd, cur_name);
311  gain_acct_guid = kvp_value_get_guid (vvv);
312 
313  gain_acct = xaccAccountLookup (gain_acct_guid, qof_instance_get_book(acc));
314  return gain_acct;
315 }
QofBook * qof_instance_get_book(gconstpointer inst)
Definition: qofinstance.c:548
Definition: guid.h:54
GncGUID * kvp_value_get_guid(const KvpValue *value)
Definition: kvp_frame.c:1160
const char * gnc_commodity_get_unique_name(const gnc_commodity *cm)
Account * xaccAccountLookup(const GncGUID *guid, QofBook *book)
Definition: Account.c:1690

◆ xaccAccountHasTrades()

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 existence 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 (acc_comm != t->common_currency) return TRUE;
97  }
98 
99  return FALSE;
100 }
SplitList * xaccAccountGetSplitList(const Account *acc)
Definition: Account.c:3603
gboolean xaccAccountIsPriced(const Account *acc)
Definition: Account.c:4135
GList SplitList
Definition: gnc-engine.h:203
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Definition: Account.c:3034

◆ xaccAccountSetDefaultGainAccount()

void xaccAccountSetDefaultGainAccount ( Account acc,
const Account gains_acct 
)

The xaccAccountSetDefaultGainAccount() routine can be used to set the account to which realized gains/losses will be posted by default. This routine does nothing more than set value of the "/lot-mgmt/gains-act/XXX" key, where XXX is the unique currency name of the currency of gains account.

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

270 {
271  KvpFrame *cwd;
272  KvpValue *vvv;
273  const char * cur_name;
274  gnc_commodity *acc_comm;
275 
276  if (!acc || !gain_acct) return;
277 
278  cwd = xaccAccountGetSlots (acc);
279  cwd = kvp_frame_get_frame_slash (cwd, "/lot-mgmt/gains-act/");
280 
281  /* Accounts are indexed by thier unique currency name */
282  acc_comm = xaccAccountGetCommodity(acc);
283  cur_name = gnc_commodity_get_unique_name (acc_comm);
284 
285  xaccAccountBeginEdit (acc);
286  vvv = kvp_value_new_guid (xaccAccountGetGUID (gain_acct));
287  kvp_frame_set_slot_nc (cwd, cur_name, vvv);
288  qof_instance_set_slots(QOF_INSTANCE(acc), acc->inst.kvp_data);
289  xaccAccountCommitEdit (acc);
290 }
#define xaccAccountGetGUID(X)
Definition: Account.h:239
void kvp_frame_set_slot_nc(KvpFrame *frame, const gchar *key, KvpValue *value)
void xaccAccountBeginEdit(Account *acc)
Definition: Account.c:1143
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Definition: Account.c:3034
const char * gnc_commodity_get_unique_name(const gnc_commodity *cm)
void xaccAccountCommitEdit(Account *acc)
Definition: Account.c:1184

◆ xaccSplitAssign()

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 582 of file cap-gains.c.

583 {
584  Account *acc;
585  gboolean splits_split_up = FALSE;
586  GNCLot *lot;
587  GNCPolicy *pcy;
588 
589  if (!split) return FALSE;
590 
591  /* If this split already belongs to a lot or the account doesn't
592  * have lots, we are done.
593  */
594  if (split->lot) return FALSE;
595  acc = split->acc;
596  if (!xaccAccountHasTrades (acc))
597  return FALSE;
598  if (gnc_numeric_zero_p (split->amount))
599  return FALSE;
600 
601  ENTER ("(split=%p)", split);
602 
603  pcy = gnc_account_get_policy(acc);
604  xaccAccountBeginEdit (acc);
605 
606  /* If we are here, this split does not belong to any lot.
607  * We ask the policy for a lot to assign it to. This
608  * block is written in the form of a while loop, since we
609  * may have to bust a split across several lots.
610  */
611  while (split)
612  {
613  PINFO ("have split %p amount=%s", split,
614  gnc_num_dbg_to_string (split->amount));
615  split->gains |= GAINS_STATUS_VDIRTY;
616  lot = pcy->PolicyGetLot (pcy, split);
617  if (!lot)
618  {
619  lot = gnc_lot_make_default (acc);
620  PINFO ("start new lot (%s)", gnc_lot_get_title(lot));
621  }
622  split = xaccSplitAssignToLot (split, lot);
623  if (split) splits_split_up = TRUE;
624  }
625  xaccAccountCommitEdit (acc);
626 
627  LEAVE (" split_up=%d", splits_split_up);
628  return splits_split_up;
629 }
gchar * gnc_num_dbg_to_string(gnc_numeric n)
Definition: gnc-numeric.c:1383
#define PINFO(format, args...)
Definition: qoflog.h:244
gboolean xaccAccountHasTrades(const Account *acc)
Definition: cap-gains.c:79
gboolean gnc_numeric_zero_p(gnc_numeric a)
Definition: gnc-numeric.c:173
#define ENTER(format, args...)
Definition: qoflog.h:256
const char * gnc_lot_get_title(const GNCLot *lot)
Definition: gnc-lot.c:368
Split * xaccSplitAssignToLot(Split *split, GNCLot *lot)
Definition: cap-gains.c:365
GNCLot * gnc_lot_make_default(Account *acc)
Definition: gnc-lot.c:681
void xaccAccountBeginEdit(Account *acc)
Definition: Account.c:1143
#define LEAVE(format, args...)
Definition: qoflog.h:266
void xaccAccountCommitEdit(Account *acc)
Definition: Account.c:1184
GNCPolicy * gnc_account_get_policy(Account *acc)
Definition: Account.c:1743

◆ xaccSplitAssignToLot()

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 365 of file cap-gains.c.

366 {
367  Account *acc;
368  gnc_numeric baln;
369  int cmp;
370  gboolean baln_is_positive, amt_is_positive;
371 
372  if (!lot) return split;
373  if (!split) return NULL;
374 
375  /* If this split already belongs to a lot, we are done. */
376  if (split->lot) return NULL;
377 
378  /* Anomolous situation; except for voided transactions,
379  * we don't expect to see splits with no amount ..
380  * unless they're gains splits, and we shouldn't see those.
381  */
382  if (gnc_numeric_zero_p (split->amount))
383  {
384  if (xaccTransGetVoidStatus(split->parent)) return NULL;
385 
386  PWARN ("split with zero amount; value=%s gflag=%x gsplit=%p",
387  gnc_num_dbg_to_string (split->amount),
388  split->gains,
389  split->gains_split);
390  if (split->gains_split)
391  {
392  PWARN ("gains amt=%s value=%s",
393  gnc_num_dbg_to_string (split->gains_split->amount),
394  gnc_num_dbg_to_string (split->gains_split->value));
395  }
396  return NULL;
397  }
398 
399  /* If the lot is closed, we can't add anything to it */
400  baln = gnc_lot_get_balance (lot);
401  if (gnc_lot_is_closed (lot)) return split;
402 
403  /* If the lot balance is zero, but the lot is open, then
404  * the lot is empty. Unconditionally add the split. */
405  if (gnc_numeric_zero_p (baln))
406  {
407  acc = split->acc;
408  xaccAccountBeginEdit (acc);
409  gnc_lot_add_split (lot, split);
410  PINFO ("added split to empty lot, new lot baln=%s (%s)",
412  gnc_lot_get_title (lot));
413  xaccAccountCommitEdit (acc);
414  return NULL;
415  }
416 
417  /* If the sign of the split is the same as the sign of the lot,
418  * add the split, but complain about it ... none of the currently
419  * implemented accounting policies should be giving us splits
420  * that make lots larger. One a lot is open, the FIFO/LIFO
421  * policies should be working only to make the lot smaller.
422  * We can remove the warning emssage come the day we have
423  * fancier policies.
424  */
425  baln_is_positive = gnc_numeric_positive_p (baln);
426  amt_is_positive = gnc_numeric_positive_p (split->amount);
427  if ((baln_is_positive && amt_is_positive) ||
428  ((!baln_is_positive) && (!amt_is_positive)))
429  {
430  PWARN ("accounting policy gave us split that enlarges the lot!\n"
431  "old lot baln=%s split amt=%s lot=%s",
433  gnc_num_dbg_to_string (split->amount),
434  gnc_lot_get_title (lot));
435 
436  acc = split->acc;
437  xaccAccountBeginEdit (acc);
438  gnc_lot_add_split (lot, split);
439  xaccAccountCommitEdit (acc);
440  return NULL;
441  }
442 
443  /* If adding the split would make the lot balance change sign,
444  * then we split the split into two pieces: one piece that will
445  * bring the lot balance to zero, and another to be dealt with
446  * later. */
447  cmp = gnc_numeric_compare (gnc_numeric_abs(split->amount),
448  gnc_numeric_abs(baln));
449 
450  PINFO ("found open lot with baln=%s (%s)", gnc_num_dbg_to_string (baln),
451  gnc_lot_get_title (lot));
452 
453  /* cmp == -1 if amt < baln, ==0 if amt==baln */
454  if (0 >= cmp)
455  {
456  acc = split->acc;
457  xaccAccountBeginEdit (acc);
458  gnc_lot_add_split (lot, split);
459  PINFO ("simple added split to lot, new lot baln=%s",
461  xaccAccountCommitEdit (acc);
462  return NULL;
463  }
464 
465  /* If we are here, then (cmp == +1 iff (amt > baln)) and we need
466  * to split up the split into pieces. Do it. */
467  {
468  time64 now = gnc_time (NULL);
469  Split * new_split;
470  gnc_numeric amt_a, amt_b, amt_tot;
471  gnc_numeric val_a, val_b, val_tot;
472  gnc_numeric frac;
473  Transaction *trans;
474  Timespec ts;
475 
476  acc = split->acc;
477  xaccAccountBeginEdit (acc);
478  trans = split->parent;
479  xaccTransBeginEdit (trans);
480 
481  amt_tot = split->amount;
482  amt_a = gnc_numeric_neg (baln);
483  amt_b = gnc_numeric_sub_fixed (amt_tot, amt_a);
484 
485  PINFO ("++++++++++++++ splitting split=%p into amt = %s + %s",
486  split,
487  gnc_num_dbg_to_string(amt_a),
488  gnc_num_dbg_to_string(amt_b) );
489 
490  /* Compute the value so that it holds in the same proportion:
491  * i.e. so that (amt_a / amt_tot) = (val_a / val_tot)
492  */
493  val_tot = split->value;
494  frac = gnc_numeric_div (amt_a, amt_tot,
496  val_a = gnc_numeric_mul (frac, val_tot,
497  gnc_numeric_denom(val_tot),
499 
500  val_b = gnc_numeric_sub_fixed (val_tot, val_a);
501  if (gnc_numeric_check(val_a))
502  {
503  PERR("Numeric overflow\n"
504  "Acct=%s Txn=%s\n"
505  "\tval_tot=%s amt_a=%s amt_tot=%s\n",
506  xaccAccountGetName(acc),
508  gnc_num_dbg_to_string(val_tot),
509  gnc_num_dbg_to_string(amt_a),
510  gnc_num_dbg_to_string(amt_tot));
511  }
512 
513  if (gnc_numeric_zero_p(val_a) || gnc_numeric_zero_p(val_b))
514  {
515  PERR ("Failed to split into two!");
516  }
517 
518  PINFO ("split value is = %s = %s + %s",
519  gnc_num_dbg_to_string(val_tot),
520  gnc_num_dbg_to_string(val_a),
521  gnc_num_dbg_to_string(val_b) );
522 
523  xaccSplitSetAmount (split, amt_a);
524  xaccSplitSetValue (split, val_a);
525 
526  /* Adding this split will have the effect of closing this lot,
527  * because the new balance should be precisely zero. */
528  gnc_lot_add_split (lot, split);
529 
530  /* Put the remainder of the balance into a new split,
531  * which is in other respects just a clone of this one. */
532  new_split = xaccMallocSplit (qof_instance_get_book(acc));
533 
534  /* Copy most of the split attributes */
535  xaccSplitSetMemo (new_split, xaccSplitGetMemo (split));
536  /* Set split-action with gnc_set_num_action which is the same as
537  * xaccSplitSetAction with these arguments; use gnc_get_num_action to get
538  * split-action which is the same as xaccSplitGetAction */
539  gnc_set_num_action(NULL, new_split, NULL, gnc_get_num_action(NULL, split));
540  xaccSplitSetReconcile (new_split, xaccSplitGetReconcile (split));
541  ts = xaccSplitRetDateReconciledTS (split);
542  xaccSplitSetDateReconciledTS (new_split, &ts);
543 
544  /* We do not copy the KVP tree, as it seems like a dangerous
545  * thing to do. If the user wants to access stuff in the 'old'
546  * kvp tree from the 'new' split, they shoudl follow the
547  * 'split-lot' pointers. Yes, this is complicated, but what
548  * else can one do ??
549  */
550  /* Add kvp markup to indicate that these two splits used
551  * to be one before being 'split'
552  */
553  gnc_kvp_bag_add (split->inst.kvp_data, "lot-split", now,
554  "peer_guid", xaccSplitGetGUID (new_split),
555  NULL);
556 
557  gnc_kvp_bag_add (new_split->inst.kvp_data, "lot-split", now,
558  "peer_guid", xaccSplitGetGUID (split),
559  NULL);
560 
561  xaccAccountInsertSplit (acc, new_split);
562  xaccTransAppendSplit (trans, new_split);
563  /* Set the amount and value after the split is in the transaction
564  so it can find the correct denominator to use. Otherwise it
565  uses 100000 which may cause an overflow in some of the tests
566  in test-period */
567  xaccSplitSetAmount (new_split, amt_b);
568  xaccSplitSetValue (new_split, val_b);
569  xaccTransCommitEdit (trans);
570  xaccAccountCommitEdit (acc);
571  return new_split;
572  }
573 }
void xaccSplitSetValue(Split *s, gnc_numeric amt)
Definition: Split.c:1074
#define xaccTransAppendSplit(t, s)
Definition: Transaction.h:351
gchar * gnc_num_dbg_to_string(gnc_numeric n)
Definition: gnc-numeric.c:1383
QofBook * qof_instance_get_book(gconstpointer inst)
Definition: qofinstance.c:548
#define PINFO(format, args...)
Definition: qoflog.h:244
gnc_numeric gnc_numeric_neg(gnc_numeric a)
Definition: gnc-numeric.c:745
Timespec xaccSplitRetDateReconciledTS(const Split *split)
Definition: Split.c:1639
char xaccSplitGetReconcile(const Split *split)
Definition: Split.c:1735
void gnc_lot_add_split(GNCLot *lot, Split *split)
Definition: gnc-lot.c:495
gboolean gnc_numeric_zero_p(gnc_numeric a)
Definition: gnc-numeric.c:173
void xaccSplitSetReconcile(Split *split, char recn)
Definition: Split.c:1581
int gnc_numeric_compare(gnc_numeric a, gnc_numeric b)
Definition: gnc-numeric.c:246
#define PERR(format, args...)
Definition: qoflog.h:232
const char * gnc_lot_get_title(const GNCLot *lot)
Definition: gnc-lot.c:368
#define PWARN(format, args...)
Definition: qoflog.h:238
void xaccSplitSetAmount(Split *s, gnc_numeric amt)
Definition: Split.c:1042
gnc_numeric gnc_numeric_mul(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Definition: gnc-numeric.c:492
void xaccSplitSetMemo(Split *split, const char *memo)
Definition: Split.c:1529
const char * xaccTransGetDescription(const Transaction *trans)
Definition: Transaction.c:2188
gnc_numeric gnc_numeric_abs(gnc_numeric a)
Definition: gnc-numeric.c:760
void xaccTransCommitEdit(Transaction *trans)
Definition: Transaction.c:1555
gnc_numeric gnc_numeric_div(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Definition: gnc-numeric.c:612
#define xaccSplitGetGUID(X)
Definition: Split.h:542
void xaccTransBeginEdit(Transaction *trans)
Definition: Transaction.c:1356
gboolean gnc_numeric_positive_p(gnc_numeric a)
Definition: gnc-numeric.c:221
Split * xaccMallocSplit(QofBook *book)
Definition: Split.c:349
gboolean xaccTransGetVoidStatus(const Transaction *trans)
Definition: Transaction.c:2525
gboolean gnc_lot_is_closed(GNCLot *lot)
Definition: gnc-lot.c:301
void xaccAccountBeginEdit(Account *acc)
Definition: Account.c:1143
#define xaccAccountInsertSplit(acc, s)
Definition: Account.h:967
void xaccSplitSetDateReconciledTS(Split *split, Timespec *ts)
Definition: Split.c:1620
time64 gnc_time(time64 *tbuf)
get the current local time
Definition: gnc-date.c:399
GNCNumericErrorCode gnc_numeric_check(gnc_numeric in)
Definition: gnc-numeric.c:83
const char * xaccSplitGetMemo(const Split *split)
Definition: Split.c:1723
gint64 time64
Definition: gnc-date.h:79
const char * xaccAccountGetName(const Account *acc)
Definition: Account.c:2906
KvpFrame * gnc_kvp_bag_add(KvpFrame *kvp_root, const char *path, time64 secs, const char *first_name,...)
Definition: kvp-util.c:74
#define GNC_DENOM_AUTO
Definition: gnc-numeric.h:242
void xaccAccountCommitEdit(Account *acc)
Definition: Account.c:1184
gnc_numeric gnc_lot_get_balance(GNCLot *lot)
Definition: gnc-lot.c:404

◆ xaccSplitComputeCapGains()

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 680 of file cap-gains.c.

681 {
682  SplitList *node;
683  GNCLot *lot;
684  GNCPolicy *pcy;
685  gnc_commodity *currency = NULL;
686  gnc_numeric zero = gnc_numeric_zero();
687  gnc_numeric value = zero;
688  gnc_numeric frac;
689  gnc_numeric opening_amount, opening_value;
690  gnc_numeric lot_amount, lot_value;
691  gnc_commodity *opening_currency;
692 
693  if (!split) return;
694  lot = split->lot;
695  if (!lot) return;
697  currency = split->parent->common_currency;
698 
699  ENTER ("(split=%p gains=%p status=0x%x lot=%s)", split,
700  split->gains_split, split->gains,
701  kvp_frame_get_string (gnc_lot_get_slots (lot), "/title"));
702 
703  /* Make sure the status flags and pointers are initialized */
704  xaccSplitDetermineGainStatus(split);
705 
706  /* Not possible to have gains if the transaction currency and
707  * account commodity are identical. */
708  if (gnc_commodity_equal (currency,
709  xaccAccountGetCommodity(split->acc)))
710  {
711  LEAVE ("Currency transfer, gains not possible, returning.");
712  return;
713  }
714 
715  if (pcy->PolicyIsOpeningSplit (pcy, lot, split))
716  {
717 #if MOVE_THIS_TO_A_DATA_INTEGRITY_SCRUBBER
718  /* Check to make sure that this opening split doesn't
719  * have a cap-gain transaction associated with it.
720  * If it does, that's wrong, and we ruthlessly destroy it.
721  * XXX Don't do this, it leads to infinite loops.
722  * We need to scrub out errors like this elsewhere!
723  */
724  if (xaccSplitGetCapGainsSplit (split))
725  {
726  Split *gains_split = xaccSplitGetCapGainsSplit(split);
727  Transaction *trans = gains_split->parent;
728  PERR ("Opening Split must not have cap gains!!\n");
729 
730  xaccTransBeginEdit (trans);
731  xaccTransDestroy (trans);
732  xaccTransCommitEdit (trans);
733  }
734 #endif
735  LEAVE ("Lot opening split, returning.");
736  return;
737  }
738 
739  if (g_strcmp0 ("stock-split", xaccSplitGetType (split)) == 0)
740  {
741  LEAVE ("Stock split split, returning.");
742  return;
743  }
744 
745  if (GAINS_STATUS_GAINS & split->gains)
746  {
747  Split *s;
748  PINFO ("split is a gains recording split, switch over");
749  /* If this is the split that records the gains, then work with
750  * the split that generates the gains.
751  */
752  /* split = xaccSplitGetCapGainsSplit (split); */
753  s = split->gains_split;
754 
755  /* This should never be NULL, and if it is, and its matching
756  * parent can't be found, then its a bug, and we should be
757  * discarding this split. But ... for now .. return.
758  * XXX move appropriate actions to a 'scrub' routine'
759  */
760  if (!s)
761  {
762  PERR ("Bad gains-split pointer! .. trying to recover.");
763  split->gains_split = xaccSplitGetCapGainsSplit (split);
764  s = split->gains_split;
765  if (!s) return;
766 #if MOVE_THIS_TO_A_DATA_INTEGRITY_SCRUBBER
767  xaccTransDestroy (trans);
768 #endif
769  }
770  split = s;
771  }
772 
773  /* Note: if the value of the 'opening' split(s) has changed,
774  * then the cap gains are changed. So we need to check not
775  * only if this split is dirty, but also the lot-opening splits. */
776  for (node = gnc_lot_get_split_list(lot); node; node = node->next)
777  {
778  Split *s = node->data;
779  if (pcy->PolicyIsOpeningSplit(pcy, lot, s))
780  {
781  if (GAINS_STATUS_UNKNOWN == s->gains) xaccSplitDetermineGainStatus (s);
782  if (s->gains & GAINS_STATUS_VDIRTY)
783  {
784  /* Force a recompute to occur */
785  split->gains |= GAINS_STATUS_VDIRTY;
786  break;
787  }
788  }
789  }
790 
791  /* If it doesn't look like this split is 'dirty', then there's
792  * nothing to do. Just return. */
793  if ((FALSE == (split->gains & GAINS_STATUS_A_VDIRTY)) &&
794  (split->gains_split) &&
795  (FALSE == (split->gains_split->gains & GAINS_STATUS_A_VDIRTY)))
796  {
797  LEAVE ("split not dirty, returning");
798  return;
799  }
800 
801  /* Yow! If amount is zero, there's nothing to do! Amount-zero splits
802  * may exist if users attempted to manually record gains. */
803  if (gnc_numeric_zero_p (split->amount)) return;
804 
805  /* If we got to here, then the split or something related is
806  * 'dirty' and the gains really do need to be recomputed.
807  * So start working things. */
808 
809  /* Get the amount and value in this lot at the time of this transaction. */
810  gnc_lot_get_balance_before (lot, split, &lot_amount, &lot_value);
811 
812  pcy->PolicyGetLotOpening (pcy, lot, &opening_amount, &opening_value,
813  &opening_currency);
814 
815  /* Check to make sure the lot-opening currency and this split
816  * use the same currency */
817  if (FALSE == gnc_commodity_equiv (currency, opening_currency))
818  {
819  /* OK, the purchase and the sale were made in different currencies.
820  * I don't know how to compute cap gains for that. This is not
821  * an error. Just punt, silently.
822  */
823  LEAVE ("Can't compute gains, mismatched commodities!");
824  return;
825  }
826 
827  /* Opening amount should be larger (or equal) to current split,
828  * and it should be of the opposite sign.
829  * XXX This should really be a part of a scrub routine that
830  * cleans up the lot, before we get at it!
831  */
832  if (0 > gnc_numeric_compare (gnc_numeric_abs(lot_amount),
833  gnc_numeric_abs(split->amount)))
834  {
835  GList *n;
836  for (n = gnc_lot_get_split_list(lot); n; n = n->next)
837  {
838  Split *s = n->data;
839  PINFO ("split amt=%s", gnc_num_dbg_to_string(s->amount));
840  }
841  PERR ("Malformed Lot \"%s\"! (too thin!) "
842  "opening amt=%s split amt=%s baln=%s",
843  gnc_lot_get_title (lot),
844  gnc_num_dbg_to_string (lot_amount),
845  gnc_num_dbg_to_string (split->amount),
847  return;
848  }
849  if ( (gnc_numeric_negative_p(lot_amount) ||
850  gnc_numeric_positive_p(split->amount)) &&
851  (gnc_numeric_positive_p(lot_amount) ||
852  gnc_numeric_negative_p(split->amount)))
853  {
854  GList *n;
855  for (n = gnc_lot_get_split_list(lot); n; n = n->next)
856  {
857  Split *s = n->data;
858  PINFO ("split amt=%s", gnc_num_dbg_to_string(s->amount));
859  }
860  PERR ("Malformed Lot \"%s\"! (too fat!) "
861  "opening amt=%s split amt=%s baln=%s",
862  gnc_lot_get_title (lot),
863  gnc_num_dbg_to_string (lot_amount),
864  gnc_num_dbg_to_string (split->amount),
866  return;
867  }
868 
869  /* The cap gains is the difference between the basis prior to the
870  * current split, and the current split, pro-rated for an equal
871  * amount of shares.
872  * i.e. purchase_price = lot_value / lot_amount
873  * cost_basis = purchase_price * current_split_amount
874  * cap_gain = current_split_value - cost_basis
875  */
876  /* Fraction of the lot that this split represents: */
877  frac = gnc_numeric_div (split->amount, lot_amount,
880  /* Basis for this split: */
881  value = gnc_numeric_mul (frac, lot_value,
882  gnc_numeric_denom(opening_value),
884  /* Capital gain for this split: */
885  value = gnc_numeric_sub (value, split->value,
887  PINFO ("Open amt=%s val=%s; split amt=%s val=%s; gains=%s\n",
888  gnc_num_dbg_to_string (lot_amount),
889  gnc_num_dbg_to_string (lot_value),
890  gnc_num_dbg_to_string (split->amount),
891  gnc_num_dbg_to_string (split->value),
892  gnc_num_dbg_to_string (value));
893  if (gnc_numeric_check (value))
894  {
895  PERR ("Numeric overflow during gains calculation\n"
896  "Acct=%s Txn=%s\n"
897  "\tOpen amt=%s val=%s\n\tsplit amt=%s val=%s\n\tgains=%s\n",
898  xaccAccountGetName(split->acc),
899  xaccTransGetDescription(split->parent),
900  gnc_num_dbg_to_string (lot_amount),
901  gnc_num_dbg_to_string (lot_value),
902  gnc_num_dbg_to_string (split->amount),
903  gnc_num_dbg_to_string (split->value),
904  gnc_num_dbg_to_string (value));
905  return;
906  }
907 
908  /* Are the cap gains zero? If not, add a balancing transaction.
909  * As per design doc lots.txt: the transaction has two splits,
910  * with equal & opposite values. The amt of one iz zero (so as
911  * not to upset the lot balance), the amt of the other is the same
912  * as its value (its the realized gain/loss).
913  */
914  if (FALSE == gnc_numeric_zero_p (value))
915  {
916  Transaction *trans;
917  Split *lot_split, *gain_split;
918  Timespec ts;
919  gboolean new_gain_split;
920  gnc_numeric negvalue = gnc_numeric_neg (value);
921 
922  /* See if there already is an associated gains transaction.
923  * If there is, adjust its value as appropriate. Else, create
924  * a new gains transaction.
925  */
926  /* lot_split = xaccSplitGetCapGainsSplit (split); */
927  lot_split = split->gains_split;
928 
929  if (NULL == lot_split)
930  {
931  Account *lot_acc = gnc_lot_get_account(lot);
932  QofBook *book = qof_instance_get_book(lot_acc);
933  Transaction *base_txn = xaccSplitGetParent (split);
934 
935  new_gain_split = TRUE;
936 
937  lot_split = xaccMallocSplit (book);
938  gain_split = xaccMallocSplit (book);
939 
940  /* Check to make sure the gains account currency matches. */
941  if ((NULL == gain_acc) ||
942  (FALSE == gnc_commodity_equiv (currency,
943  xaccAccountGetCommodity(gain_acc))))
944  {
945  gain_acc = GetOrMakeGainAcct (lot_acc, currency);
946  }
947 
948  xaccAccountBeginEdit (gain_acc);
949  xaccAccountInsertSplit (gain_acc, gain_split);
950  xaccAccountCommitEdit (gain_acc);
951 
952  xaccAccountBeginEdit (lot_acc);
953  xaccAccountInsertSplit (lot_acc, lot_split);
954  xaccAccountCommitEdit (lot_acc);
955 
956  trans = xaccMallocTransaction (book);
957 
958  xaccTransBeginEdit (trans);
959  xaccTransSetCurrency (trans, currency);
960  xaccTransSetDescription (trans, _("Realized Gain/Loss"));
961 
962  xaccTransAppendSplit (trans, lot_split);
963  xaccTransAppendSplit (trans, gain_split);
964 
965  xaccSplitSetMemo (lot_split, _("Realized Gain/Loss"));
966  xaccSplitSetMemo (gain_split, _("Realized Gain/Loss"));
967 
968  /* For the new transaction, install KVP markup indicating
969  * that this is the gains transaction that corresponds
970  * to the gains source.
971  */
972  xaccTransBeginEdit (base_txn);
973  kvp_frame_set_guid (split->inst.kvp_data, "gains-split",
974  xaccSplitGetGUID (lot_split));
975  qof_instance_set_dirty (QOF_INSTANCE (split));
976  xaccTransCommitEdit (base_txn);
977  kvp_frame_set_guid (lot_split->inst.kvp_data, "gains-source",
978  xaccSplitGetGUID (split));
979 
980  }
981  else
982  {
983  trans = lot_split->parent;
984  gain_split = xaccSplitGetOtherSplit (lot_split);
985 
986  /* If the gains transaction has been edited so that it no longer has
987  just two splits, ignore it and assume it's still correct. */
988  if (!gain_split)
989  {
990  new_gain_split = FALSE;
991  }
992  /* If the gain is already recorded corectly do nothing. This is
993  * more than just an optimization since this may be called during
994  * gnc_book_partition_txn and depending on the order in which things
995  * happen some splits may be in the wrong book at that time. */
996  else if (split->gains_split == lot_split &&
997  lot_split->gains_split == split &&
998  gain_split->gains_split == split &&
999  gnc_numeric_equal (xaccSplitGetValue (lot_split), value) &&
1000  gnc_numeric_zero_p (xaccSplitGetAmount (lot_split)) &&
1001  gnc_numeric_equal (xaccSplitGetValue (gain_split), negvalue) &&
1002  gnc_numeric_equal (xaccSplitGetAmount (gain_split), negvalue))
1003  {
1004  new_gain_split = FALSE;
1005  }
1006  else
1007  {
1008  new_gain_split = TRUE;
1009  xaccTransBeginEdit (trans);
1010 
1011  /* Make sure the existing gains trans has the correct currency,
1012  * just in case someone screwed with it! */
1013  if (FALSE == gnc_commodity_equiv(currency, trans->common_currency))
1014  {
1015  PWARN ("Resetting the transaction currency!");
1016  xaccTransSetCurrency (trans, currency);
1017  }
1018  }
1019  }
1020 
1021  if (new_gain_split)
1022  {
1023  /* Common to both */
1024  ts = xaccTransRetDatePostedTS (split->parent);
1025  xaccTransSetDatePostedTS (trans, &ts);
1026  xaccTransSetDateEnteredSecs (trans, gnc_time (NULL));
1027 
1028  xaccSplitSetAmount (lot_split, zero);
1029  xaccSplitSetValue (lot_split, value);
1030 
1031  xaccSplitSetAmount (gain_split, negvalue);
1032  xaccSplitSetValue (gain_split, negvalue);
1033 
1034  /* Some short-cuts to help avoid the above kvp lookup. */
1035  split->gains = GAINS_STATUS_CLEAN;
1036  split->gains_split = lot_split;
1037  lot_split->gains = GAINS_STATUS_GAINS;
1038  lot_split->gains_split = split;
1039  gain_split->gains = GAINS_STATUS_GAINS;
1040  gain_split->gains_split = split;
1041 
1042  /* Do this last since it may generate an event that will call us
1043  recursively. */
1044  gnc_lot_add_split (lot, lot_split);
1045 
1046  xaccTransCommitEdit (trans);
1047  }
1048  }
1049  LEAVE ("(lot=%s)", gnc_lot_get_title(lot));
1050 }
void xaccSplitSetValue(Split *s, gnc_numeric amt)
Definition: Split.c:1074
#define xaccTransAppendSplit(t, s)
Definition: Transaction.h:351
Transaction * xaccMallocTransaction(QofBook *book)
Definition: Transaction.c:449
gboolean gnc_numeric_equal(gnc_numeric a, gnc_numeric b)
Definition: gnc-numeric.c:304
gchar * gnc_num_dbg_to_string(gnc_numeric n)
Definition: gnc-numeric.c:1383
QofBook * qof_instance_get_book(gconstpointer inst)
Definition: qofinstance.c:548
#define PINFO(format, args...)
Definition: qoflog.h:244
gnc_numeric gnc_numeric_neg(gnc_numeric a)
Definition: gnc-numeric.c:745
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:495
void xaccTransSetDescription(Transaction *trans, const char *desc)
Definition: Transaction.c:2067
gboolean gnc_numeric_zero_p(gnc_numeric a)
Definition: gnc-numeric.c:173
Transaction * xaccSplitGetParent(const Split *split)
Definition: Split.c:1658
int gnc_numeric_compare(gnc_numeric a, gnc_numeric b)
Definition: gnc-numeric.c:246
void qof_instance_set_dirty(QofInstance *inst)
Set the dirty flag.
Definition: qofinstance.c:723
#define PERR(format, args...)
Definition: qoflog.h:232
#define ENTER(format, args...)
Definition: qoflog.h:256
Split * xaccSplitGetCapGainsSplit(const Split *split)
Definition: cap-gains.c:634
gboolean gnc_numeric_negative_p(gnc_numeric a)
Definition: gnc-numeric.c:197
void xaccTransSetCurrency(Transaction *trans, gnc_commodity *curr)
Definition: Transaction.c:1327
const char * gnc_lot_get_title(const GNCLot *lot)
Definition: gnc-lot.c:368
void xaccTransDestroy(Transaction *trans)
Definition: Transaction.c:1378
#define PWARN(format, args...)
Definition: qoflog.h:238
GList SplitList
Definition: gnc-engine.h:203
void xaccSplitSetAmount(Split *s, gnc_numeric amt)
Definition: Split.c:1042
gnc_numeric gnc_numeric_mul(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Definition: gnc-numeric.c:492
void xaccSplitSetMemo(Split *split, const char *memo)
Definition: Split.c:1529
SplitList * gnc_lot_get_split_list(const GNCLot *lot)
Definition: gnc-lot.c:348
const char * xaccTransGetDescription(const Transaction *trans)
Definition: Transaction.c:2188
void gnc_lot_get_balance_before(const GNCLot *lot, const Split *split, gnc_numeric *amount, gnc_numeric *value)
Definition: gnc-lot.c:445
gnc_numeric gnc_numeric_abs(gnc_numeric a)
Definition: gnc-numeric.c:760
void xaccTransCommitEdit(Transaction *trans)
Definition: Transaction.c:1555
gnc_numeric gnc_numeric_div(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Definition: gnc-numeric.c:612
#define xaccSplitGetGUID(X)
Definition: Split.h:542
void xaccTransBeginEdit(Transaction *trans)
Definition: Transaction.c:1356
gboolean gnc_numeric_positive_p(gnc_numeric a)
Definition: gnc-numeric.c:221
Split * xaccMallocSplit(QofBook *book)
Definition: Split.c:349
gnc_numeric gnc_numeric_sub(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Definition: gnc-numeric.c:473
gnc_numeric xaccSplitGetValue(const Split *split)
Definition: Split.c:1748
void xaccAccountBeginEdit(Account *acc)
Definition: Account.c:1143
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Definition: Account.c:3034
#define xaccAccountInsertSplit(acc, s)
Definition: Account.h:967
Timespec xaccTransRetDatePostedTS(const Transaction *trans)
Definition: Transaction.c:2247
Split * xaccSplitGetOtherSplit(const Split *split)
Definition: Split.c:1841
#define LEAVE(format, args...)
Definition: qoflog.h:266
KvpFrame * gnc_lot_get_slots(const GNCLot *lot)
Definition: gnc-lot.c:342
time64 gnc_time(time64 *tbuf)
get the current local time
Definition: gnc-date.c:399
GNCNumericErrorCode gnc_numeric_check(gnc_numeric in)
Definition: gnc-numeric.c:83
Account * gnc_lot_get_account(const GNCLot *lot)
Definition: gnc-lot.c:311
void xaccTransSetDateEnteredSecs(Transaction *trans, time64 secs)
Definition: Transaction.c:1927
const char * xaccSplitGetType(const Split *s)
Definition: Split.c:1803
const char * xaccAccountGetName(const Account *acc)
Definition: Account.c:2906
#define GNC_DENOM_AUTO
Definition: gnc-numeric.h:242
void xaccAccountCommitEdit(Account *acc)
Definition: Account.c:1184
GNCPolicy * gnc_account_get_policy(Account *acc)
Definition: Account.c:1743
gboolean gnc_commodity_equiv(const gnc_commodity *a, const gnc_commodity *b)
gnc_numeric gnc_lot_get_balance(GNCLot *lot)
Definition: gnc-lot.c:404
void xaccTransSetDatePostedTS(Transaction *trans, const Timespec *ts)
Definition: Transaction.c:1946
gnc_numeric xaccSplitGetAmount(const Split *split)
Definition: Split.c:1742

◆ xaccSplitGetCapGains()

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 1055 of file cap-gains.c.

1056 {
1057  if (!split) return gnc_numeric_zero();
1058  ENTER("(split=%p)", split);
1059 
1060  if (GAINS_STATUS_UNKNOWN == split->gains)
1061  xaccSplitDetermineGainStatus(split);
1062  if ((split->gains & GAINS_STATUS_A_VDIRTY) ||
1063  (split->gains_split &&
1064  (split->gains_split->gains & GAINS_STATUS_A_VDIRTY)))
1065  {
1066  xaccSplitComputeCapGains (split, NULL);
1067  }
1068 
1069  /* If this is the source split, get the gains from the one
1070  * that records the gains. If this already is the gains split,
1071  * its a no-op. */
1072  if (!(GAINS_STATUS_GAINS & split->gains))
1073  {
1074  /* split = xaccSplitGetCapGainsSplit (split); */
1075  split = split->gains_split;
1076  }
1077 
1078  LEAVE("(split=%p)", split);
1079  if (!split) return gnc_numeric_zero();
1080 
1081  return split->value;
1082 }
void xaccSplitComputeCapGains(Split *split, Account *gain_acc)
Definition: cap-gains.c:680
#define ENTER(format, args...)
Definition: qoflog.h:256
#define LEAVE(format, args...)
Definition: qoflog.h:266

◆ xaccSplitGetCapGainsSplit()

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 634 of file cap-gains.c.

635 {
636  KvpValue *val;
637  GncGUID *gains_guid;
638  Split *gains_split;
639 
640  if (!split) return NULL;
641 
642  val = kvp_frame_get_slot (split->inst.kvp_data, "gains-split");
643  if (!val) return NULL;
644  gains_guid = kvp_value_get_guid (val);
645  if (!gains_guid) return NULL;
646 
647  /* Both splits will be in the same collection, so search there. */
648  gains_split = (Split*) qof_collection_lookup_entity (
649  qof_instance_get_collection(split), gains_guid);
650  PINFO ("split=%p has gains-split=%p", split, gains_split);
651  return gains_split;
652 }
QofInstance * qof_collection_lookup_entity(const QofCollection *col, const GncGUID *guid)
Definition: qofid.c:211
#define PINFO(format, args...)
Definition: qoflog.h:244
Definition: guid.h:54
QofCollection * qof_instance_get_collection(gconstpointer ptr)
Definition: qofinstance.c:533
GncGUID * kvp_value_get_guid(const KvpValue *value)
Definition: kvp_frame.c:1160

◆ xaccSplitGetGainsSourceSplit()

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 657 of file cap-gains.c.

658 {
659  KvpValue *val;
660  GncGUID *source_guid;
661  Split *source_split;
662 
663  if (!split) return NULL;
664 
665  val = kvp_frame_get_slot (split->inst.kvp_data, "gains-source");
666  if (!val) return NULL;
667  source_guid = kvp_value_get_guid (val);
668  if (!source_guid) return NULL;
669 
670  /* Both splits will be in the same collection, so search there. */
671  source_split = (Split*) qof_collection_lookup_entity(
672  qof_instance_get_collection(split), source_guid);
673  PINFO ("split=%p has source-split=%p", split, source_split);
674  return source_split;
675 }
QofInstance * qof_collection_lookup_entity(const QofCollection *col, const GncGUID *guid)
Definition: qofid.c:211
#define PINFO(format, args...)
Definition: qoflog.h:244
Definition: guid.h:54
QofCollection * qof_instance_get_collection(gconstpointer ptr)
Definition: qofinstance.c:533
GncGUID * kvp_value_get_guid(const KvpValue *value)
Definition: kvp_frame.c:1160