00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00056 #include "config.h"
00057
00058 #include <glib.h>
00059 #include <glib/gi18n.h>
00060
00061 #include "AccountP.h"
00062 #include "Scrub2.h"
00063 #include "Scrub3.h"
00064 #include "Transaction.h"
00065 #include "TransactionP.h"
00066 #include "cap-gains.h"
00067 #include "gnc-engine.h"
00068 #include "gnc-lot.h"
00069 #include "policy.h"
00070 #include "policy-p.h"
00071
00072 static QofLogModule log_module = GNC_MOD_LOT;
00073
00074
00075
00076
00077 gboolean
00078 xaccAccountHasTrades (const Account *acc)
00079 {
00080 gnc_commodity *acc_comm;
00081 SplitList *splits, *node;
00082
00083 if (!acc) return FALSE;
00084
00085 if (xaccAccountIsPriced (acc))
00086 return TRUE;
00087
00088 acc_comm = xaccAccountGetCommodity(acc);
00089
00090 splits = xaccAccountGetSplitList(acc);
00091 for (node = splits; node; node = node->next)
00092 {
00093 Split *s = node->data;
00094 Transaction *t = s->parent;
00095 if (acc_comm != t->common_currency) return TRUE;
00096 }
00097
00098 return FALSE;
00099 }
00100
00101
00102
00103 struct find_lot_s
00104 {
00105 GNCLot *lot;
00106 gnc_commodity *currency;
00107 Timespec ts;
00108 int (*numeric_pred)(gnc_numeric);
00109 gboolean (*date_pred)(Timespec e, Timespec tr);
00110 };
00111
00112 static gboolean
00113 earliest_pred (Timespec earl, Timespec tran)
00114 {
00115 return ((earl.tv_sec > tran.tv_sec) ||
00116 ((earl.tv_sec == tran.tv_sec) && (earl.tv_nsec > tran.tv_nsec)));
00117 }
00118
00119 static gboolean
00120 latest_pred (Timespec earl, Timespec tran)
00121 {
00122 return ((earl.tv_sec < tran.tv_sec) ||
00123 ((earl.tv_sec == tran.tv_sec) && (earl.tv_nsec < tran.tv_nsec)));
00124 }
00125
00126 static gpointer
00127 finder_helper (GNCLot *lot, gpointer user_data)
00128 {
00129 struct find_lot_s *els = user_data;
00130 Split *s;
00131 Transaction *trans;
00132 gnc_numeric bal;
00133 gboolean opening_is_positive, bal_is_positive;
00134
00135 if (gnc_lot_is_closed (lot)) return NULL;
00136
00137 s = gnc_lot_get_earliest_split (lot);
00138 if (s == NULL) return NULL;
00139
00140
00141
00142
00143
00144 if (0 == (els->numeric_pred) (s->amount)) return NULL;
00145 bal = gnc_lot_get_balance (lot);
00146 opening_is_positive = gnc_numeric_positive_p (s->amount);
00147 bal_is_positive = gnc_numeric_positive_p (bal);
00148 if (opening_is_positive != bal_is_positive) return NULL;
00149
00150 trans = s->parent;
00151 if (els->currency &&
00152 (FALSE == gnc_commodity_equiv (els->currency,
00153 trans->common_currency)))
00154 {
00155 return NULL;
00156 }
00157
00158 if (els->date_pred (els->ts, trans->date_posted))
00159 {
00160 els->ts = trans->date_posted;
00161 els->lot = lot;
00162 }
00163
00164 return NULL;
00165 }
00166
00167 static inline GNCLot *
00168 xaccAccountFindOpenLot (Account *acc, gnc_numeric sign,
00169 gnc_commodity *currency,
00170 gint64 guess,
00171 gboolean (*date_pred)(Timespec, Timespec))
00172 {
00173 struct find_lot_s es;
00174
00175 es.lot = NULL;
00176 es.currency = currency;
00177 es.ts.tv_sec = guess;
00178 es.ts.tv_nsec = 0;
00179 es.date_pred = date_pred;
00180
00181 if (gnc_numeric_positive_p(sign)) es.numeric_pred = gnc_numeric_negative_p;
00182 else es.numeric_pred = gnc_numeric_positive_p;
00183
00184 xaccAccountForEachLot (acc, finder_helper, &es);
00185 return es.lot;
00186 }
00187
00188 GNCLot *
00189 xaccAccountFindEarliestOpenLot (Account *acc, gnc_numeric sign,
00190 gnc_commodity *currency)
00191 {
00192 GNCLot *lot;
00193 ENTER (" sign=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT, sign.num,
00194 sign.denom);
00195
00196 lot = xaccAccountFindOpenLot (acc, sign, currency,
00197 G_MAXINT64, earliest_pred);
00198 LEAVE ("found lot=%p %s baln=%s", lot, gnc_lot_get_title (lot),
00199 gnc_num_dbg_to_string(gnc_lot_get_balance(lot)));
00200 return lot;
00201 }
00202
00203 GNCLot *
00204 xaccAccountFindLatestOpenLot (Account *acc, gnc_numeric sign,
00205 gnc_commodity *currency)
00206 {
00207 GNCLot *lot;
00208 ENTER (" sign=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT,
00209 sign.num, sign.denom);
00210
00211 lot = xaccAccountFindOpenLot (acc, sign, currency,
00212 G_MININT64, latest_pred);
00213 LEAVE ("found lot=%p %s", lot, gnc_lot_get_title (lot));
00214 return lot;
00215 }
00216
00217
00218
00219
00220 static Account *
00221 GetOrMakeLotOrphanAccount (Account *root, gnc_commodity * currency)
00222 {
00223 char * accname;
00224 Account * acc;
00225
00226 g_return_val_if_fail (root, NULL);
00227
00228
00229 if (!currency)
00230 {
00231 PERR ("No currency specified!");
00232 return NULL;
00233 }
00234
00235 accname = g_strconcat (_("Orphaned Gains"), "-",
00236 gnc_commodity_get_mnemonic (currency), NULL);
00237
00238
00239 acc = gnc_account_lookup_by_name(root, accname);
00240
00241 if (acc == NULL)
00242 {
00243
00244 acc = xaccMallocAccount (gnc_account_get_book(root));
00245 xaccAccountBeginEdit (acc);
00246 xaccAccountSetName (acc, accname);
00247 xaccAccountSetCommodity (acc, currency);
00248 xaccAccountSetType (acc, ACCT_TYPE_INCOME);
00249 xaccAccountSetDescription (acc, _("Realized Gain/Loss"));
00250 xaccAccountSetNotes (acc,
00251 _("Realized Gains or Losses from "
00252 "Commodity or Trading Accounts "
00253 "that haven't been recorded elsewhere."));
00254
00255
00256 gnc_account_append_child (root, acc);
00257 xaccAccountCommitEdit (acc);
00258 }
00259
00260 g_free (accname);
00261
00262 return acc;
00263 }
00264
00265
00266
00267 void
00268 xaccAccountSetDefaultGainAccount (Account *acc, const Account *gain_acct)
00269 {
00270 KvpFrame *cwd;
00271 KvpValue *vvv;
00272 const char * cur_name;
00273 gnc_commodity *acc_comm;
00274
00275 if (!acc || !gain_acct) return;
00276
00277 cwd = xaccAccountGetSlots (acc);
00278 cwd = kvp_frame_get_frame_slash (cwd, "/lot-mgmt/gains-act/");
00279
00280
00281 acc_comm = xaccAccountGetCommodity(acc);
00282 cur_name = gnc_commodity_get_unique_name (acc_comm);
00283
00284 xaccAccountBeginEdit (acc);
00285 vvv = kvp_value_new_guid (xaccAccountGetGUID (gain_acct));
00286 kvp_frame_set_slot_nc (cwd, cur_name, vvv);
00287 qof_instance_set_slots(QOF_INSTANCE(acc), acc->inst.kvp_data);
00288 xaccAccountCommitEdit (acc);
00289 }
00290
00291
00292
00293 Account *
00294 xaccAccountGetDefaultGainAccount (const Account *acc, const gnc_commodity * currency)
00295 {
00296 Account *gain_acct = NULL;
00297 KvpFrame *cwd;
00298 KvpValue *vvv;
00299 GncGUID * gain_acct_guid;
00300 const char * cur_name;
00301
00302 if (!acc || !currency) return NULL;
00303
00304 cwd = xaccAccountGetSlots (acc);
00305 cwd = kvp_frame_get_frame_slash (cwd, "/lot-mgmt/gains-act/");
00306
00307
00308 cur_name = gnc_commodity_get_unique_name (currency);
00309 vvv = kvp_frame_get_slot (cwd, cur_name);
00310 gain_acct_guid = kvp_value_get_guid (vvv);
00311
00312 gain_acct = xaccAccountLookup (gain_acct_guid, qof_instance_get_book(acc));
00313 return gain_acct;
00314 }
00315
00316
00317
00318
00319
00320
00321
00322
00323 static Account *
00324 GetOrMakeGainAcct (Account *acc, gnc_commodity * currency)
00325 {
00326 Account *gain_acct = NULL;
00327 KvpFrame *cwd;
00328 KvpValue *vvv;
00329 GncGUID * gain_acct_guid;
00330 const char * cur_name;
00331
00332 cwd = xaccAccountGetSlots (acc);
00333 cwd = kvp_frame_get_frame_slash (cwd, "/lot-mgmt/gains-act/");
00334
00335
00336 cur_name = gnc_commodity_get_unique_name (currency);
00337 vvv = kvp_frame_get_slot (cwd, cur_name);
00338 gain_acct_guid = kvp_value_get_guid (vvv);
00339
00340 gain_acct = xaccAccountLookup (gain_acct_guid, qof_instance_get_book(acc));
00341
00342
00343
00344 if (NULL == gain_acct)
00345 {
00346 Account *root;
00347
00348 xaccAccountBeginEdit (acc);
00349 root = gnc_account_get_root(acc);
00350 gain_acct = GetOrMakeLotOrphanAccount (root, currency);
00351
00352 vvv = kvp_value_new_guid (xaccAccountGetGUID (gain_acct));
00353 kvp_frame_set_slot_nc (cwd, cur_name, vvv);
00354 qof_instance_set_slots(QOF_INSTANCE(acc), acc->inst.kvp_data);
00355 xaccAccountCommitEdit (acc);
00356
00357 }
00358 return gain_acct;
00359 }
00360
00361
00362
00363 Split *
00364 xaccSplitAssignToLot (Split *split, GNCLot *lot)
00365 {
00366 Account *acc;
00367 gnc_numeric baln;
00368 int cmp;
00369 gboolean baln_is_positive, amt_is_positive;
00370
00371 if (!lot) return split;
00372 if (!split) return NULL;
00373
00374
00375 if (split->lot) return NULL;
00376
00377
00378
00379
00380
00381 if (gnc_numeric_zero_p (split->amount))
00382 {
00383 if (xaccTransGetVoidStatus(split->parent)) return NULL;
00384
00385 PWARN ("split with zero amount; value=%s gflag=%x gsplit=%p",
00386 gnc_num_dbg_to_string (split->amount),
00387 split->gains,
00388 split->gains_split);
00389 if (split->gains_split)
00390 {
00391 PWARN ("gains amt=%s value=%s",
00392 gnc_num_dbg_to_string (split->gains_split->amount),
00393 gnc_num_dbg_to_string (split->gains_split->value));
00394 }
00395 return NULL;
00396 }
00397
00398
00399 baln = gnc_lot_get_balance (lot);
00400 if (gnc_lot_is_closed (lot)) return split;
00401
00402
00403
00404 if (gnc_numeric_zero_p (baln))
00405 {
00406 acc = split->acc;
00407 xaccAccountBeginEdit (acc);
00408 gnc_lot_add_split (lot, split);
00409 PINFO ("added split to empty lot, new lot baln=%s (%s)",
00410 gnc_num_dbg_to_string (gnc_lot_get_balance(lot)),
00411 gnc_lot_get_title (lot));
00412 xaccAccountCommitEdit (acc);
00413 return NULL;
00414 }
00415
00416
00417
00418
00419
00420
00421
00422
00423
00424 baln_is_positive = gnc_numeric_positive_p (baln);
00425 amt_is_positive = gnc_numeric_positive_p (split->amount);
00426 if ((baln_is_positive && amt_is_positive) ||
00427 ((!baln_is_positive) && (!amt_is_positive)))
00428 {
00429 PWARN ("accounting policy gave us split that enlarges the lot!\n"
00430 "old lot baln=%s split amt=%s lot=%s",
00431 gnc_num_dbg_to_string (gnc_lot_get_balance(lot)),
00432 gnc_num_dbg_to_string (split->amount),
00433 gnc_lot_get_title (lot));
00434
00435 acc = split->acc;
00436 xaccAccountBeginEdit (acc);
00437 gnc_lot_add_split (lot, split);
00438 xaccAccountCommitEdit (acc);
00439 return NULL;
00440 }
00441
00442
00443
00444
00445
00446 cmp = gnc_numeric_compare (gnc_numeric_abs(split->amount),
00447 gnc_numeric_abs(baln));
00448
00449 PINFO ("found open lot with baln=%s (%s)", gnc_num_dbg_to_string (baln),
00450 gnc_lot_get_title (lot));
00451
00452
00453 if (0 >= cmp)
00454 {
00455 acc = split->acc;
00456 xaccAccountBeginEdit (acc);
00457 gnc_lot_add_split (lot, split);
00458 PINFO ("simple added split to lot, new lot baln=%s",
00459 gnc_num_dbg_to_string (gnc_lot_get_balance(lot)));
00460 xaccAccountCommitEdit (acc);
00461 return NULL;
00462 }
00463
00464
00465
00466 {
00467 time_t now = time(0);
00468 Split * new_split;
00469 gnc_numeric amt_a, amt_b, amt_tot;
00470 gnc_numeric val_a, val_b, val_tot;
00471 gnc_numeric frac;
00472 Transaction *trans;
00473 Timespec ts;
00474
00475 acc = split->acc;
00476 xaccAccountBeginEdit (acc);
00477 trans = split->parent;
00478 xaccTransBeginEdit (trans);
00479
00480 amt_tot = split->amount;
00481 amt_a = gnc_numeric_neg (baln);
00482 amt_b = gnc_numeric_sub_fixed (amt_tot, amt_a);
00483
00484 PINFO ("++++++++++++++ splitting split=%p into amt = %s + %s",
00485 split,
00486 gnc_num_dbg_to_string(amt_a),
00487 gnc_num_dbg_to_string(amt_b) );
00488
00489
00490
00491
00492 val_tot = split->value;
00493 frac = gnc_numeric_div (amt_a, amt_tot,
00494 GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE);
00495 val_a = gnc_numeric_mul (frac, val_tot,
00496 gnc_numeric_denom(val_tot),
00497 GNC_HOW_RND_ROUND_HALF_UP | GNC_HOW_DENOM_EXACT);
00498
00499 val_b = gnc_numeric_sub_fixed (val_tot, val_a);
00500 if (gnc_numeric_check(val_a))
00501 {
00502 PERR("Numeric overflow\n"
00503 "Acct=%s Txn=%s\n"
00504 "\tval_tot=%s amt_a=%s amt_tot=%s\n",
00505 xaccAccountGetName(acc),
00506 xaccTransGetDescription(trans),
00507 gnc_num_dbg_to_string(val_tot),
00508 gnc_num_dbg_to_string(amt_a),
00509 gnc_num_dbg_to_string(amt_tot));
00510 }
00511
00512 if (gnc_numeric_zero_p(val_a) || gnc_numeric_zero_p(val_b))
00513 {
00514 PERR ("Failed to split into two!");
00515 }
00516
00517 PINFO ("split value is = %s = %s + %s",
00518 gnc_num_dbg_to_string(val_tot),
00519 gnc_num_dbg_to_string(val_a),
00520 gnc_num_dbg_to_string(val_b) );
00521
00522 xaccSplitSetAmount (split, amt_a);
00523 xaccSplitSetValue (split, val_a);
00524
00525
00526
00527 gnc_lot_add_split (lot, split);
00528
00529
00530
00531 new_split = xaccMallocSplit (qof_instance_get_book(acc));
00532
00533
00534 xaccSplitSetMemo (new_split, xaccSplitGetMemo (split));
00535 xaccSplitSetAction (new_split, xaccSplitGetAction (split));
00536 xaccSplitSetReconcile (new_split, xaccSplitGetReconcile (split));
00537 ts = xaccSplitRetDateReconciledTS (split);
00538 xaccSplitSetDateReconciledTS (new_split, &ts);
00539
00540
00541
00542
00543
00544
00545
00546
00547
00548
00549 gnc_kvp_bag_add (split->inst.kvp_data, "lot-split", now,
00550 "peer_guid", xaccSplitGetGUID (new_split),
00551 NULL);
00552
00553 gnc_kvp_bag_add (new_split->inst.kvp_data, "lot-split", now,
00554 "peer_guid", xaccSplitGetGUID (split),
00555 NULL);
00556
00557 xaccAccountInsertSplit (acc, new_split);
00558 xaccTransAppendSplit (trans, new_split);
00559
00560
00561
00562
00563 xaccSplitSetAmount (new_split, amt_b);
00564 xaccSplitSetValue (new_split, val_b);
00565 xaccTransCommitEdit (trans);
00566 xaccAccountCommitEdit (acc);
00567 return new_split;
00568 }
00569 }
00570
00571
00572
00573
00574
00575
00576
00577 gboolean
00578 xaccSplitAssign (Split *split)
00579 {
00580 Account *acc;
00581 gboolean splits_split_up = FALSE;
00582 GNCLot *lot;
00583 GNCPolicy *pcy;
00584
00585 if (!split) return FALSE;
00586
00587
00588
00589
00590 if (split->lot) return FALSE;
00591 acc = split->acc;
00592 if (!xaccAccountHasTrades (acc))
00593 return FALSE;
00594 if (gnc_numeric_zero_p (split->amount))
00595 return FALSE;
00596
00597 ENTER ("(split=%p)", split);
00598
00599 pcy = gnc_account_get_policy(acc);
00600 xaccAccountBeginEdit (acc);
00601
00602
00603
00604
00605
00606
00607 while (split)
00608 {
00609 PINFO ("have split %p amount=%s", split,
00610 gnc_num_dbg_to_string (split->amount));
00611 split->gains |= GAINS_STATUS_VDIRTY;
00612 lot = pcy->PolicyGetLot (pcy, split);
00613 if (!lot)
00614 {
00615 lot = gnc_lot_make_default (acc);
00616 PINFO ("start new lot (%s)", gnc_lot_get_title(lot));
00617 }
00618 split = xaccSplitAssignToLot (split, lot);
00619 if (split) splits_split_up = TRUE;
00620 }
00621 xaccAccountCommitEdit (acc);
00622
00623 LEAVE (" split_up=%d", splits_split_up);
00624 return splits_split_up;
00625 }
00626
00627
00628
00629 Split *
00630 xaccSplitGetCapGainsSplit (const Split *split)
00631 {
00632 KvpValue *val;
00633 GncGUID *gains_guid;
00634 Split *gains_split;
00635
00636 if (!split) return NULL;
00637
00638 val = kvp_frame_get_slot (split->inst.kvp_data, "gains-split");
00639 if (!val) return NULL;
00640 gains_guid = kvp_value_get_guid (val);
00641 if (!gains_guid) return NULL;
00642
00643
00644 gains_split = (Split*) qof_collection_lookup_entity (
00645 qof_instance_get_collection(split), gains_guid);
00646 PINFO ("split=%p has gains-split=%p", split, gains_split);
00647 return gains_split;
00648 }
00649
00650
00651
00652 Split *
00653 xaccSplitGetGainsSourceSplit (const Split *split)
00654 {
00655 KvpValue *val;
00656 GncGUID *source_guid;
00657 Split *source_split;
00658
00659 if (!split) return NULL;
00660
00661 val = kvp_frame_get_slot (split->inst.kvp_data, "gains-source");
00662 if (!val) return NULL;
00663 source_guid = kvp_value_get_guid (val);
00664 if (!source_guid) return NULL;
00665
00666
00667 source_split = (Split*) qof_collection_lookup_entity(
00668 qof_instance_get_collection(split), source_guid);
00669 PINFO ("split=%p has source-split=%p", split, source_split);
00670 return source_split;
00671 }
00672
00673
00674
00675 void
00676 xaccSplitComputeCapGains(Split *split, Account *gain_acc)
00677 {
00678 SplitList *node;
00679 GNCLot *lot;
00680 GNCPolicy *pcy;
00681 gnc_commodity *currency = NULL;
00682 gnc_numeric zero = gnc_numeric_zero();
00683 gnc_numeric value = zero;
00684 gnc_numeric frac;
00685 gnc_numeric opening_amount, opening_value;
00686 gnc_numeric lot_amount, lot_value;
00687 gnc_commodity *opening_currency;
00688
00689 if (!split) return;
00690 lot = split->lot;
00691 if (!lot) return;
00692 pcy = gnc_account_get_policy(gnc_lot_get_account(lot));
00693 currency = split->parent->common_currency;
00694
00695 ENTER ("(split=%p gains=%p status=0x%x lot=%s)", split,
00696 split->gains_split, split->gains,
00697 kvp_frame_get_string (gnc_lot_get_slots (lot), "/title"));
00698
00699
00700 xaccSplitDetermineGainStatus(split);
00701
00702
00703
00704 if (gnc_commodity_equal (currency,
00705 xaccAccountGetCommodity(split->acc)))
00706 {
00707 LEAVE ("Currency transfer, gains not possible, returning.");
00708 return;
00709 }
00710
00711 if (pcy->PolicyIsOpeningSplit (pcy, lot, split))
00712 {
00713 #if MOVE_THIS_TO_A_DATA_INTEGRITY_SCRUBBER
00714
00715
00716
00717
00718
00719
00720 if (xaccSplitGetCapGainsSplit (split))
00721 {
00722 Split *gains_split = xaccSplitGetCapGainsSplit(split);
00723 Transaction *trans = gains_split->parent;
00724 PERR ("Opening Split must not have cap gains!!\n");
00725
00726 xaccTransBeginEdit (trans);
00727 xaccTransDestroy (trans);
00728 xaccTransCommitEdit (trans);
00729 }
00730 #endif
00731 LEAVE ("Lot opening split, returning.");
00732 return;
00733 }
00734
00735 if (safe_strcmp ("stock-split", xaccSplitGetType (split)) == 0)
00736 {
00737 LEAVE ("Stock split split, returning.");
00738 return;
00739 }
00740
00741 if (GAINS_STATUS_GAINS & split->gains)
00742 {
00743 Split *s;
00744 PINFO ("split is a gains recording split, switch over");
00745
00746
00747
00748
00749 s = split->gains_split;
00750
00751
00752
00753
00754
00755
00756 if (!s)
00757 {
00758 PERR ("Bad gains-split pointer! .. trying to recover.");
00759 split->gains_split = xaccSplitGetCapGainsSplit (split);
00760 s = split->gains_split;
00761 if (!s) return;
00762 #if MOVE_THIS_TO_A_DATA_INTEGRITY_SCRUBBER
00763 xaccTransDestroy (trans);
00764 #endif
00765 }
00766 split = s;
00767 }
00768
00769
00770
00771
00772 for (node = gnc_lot_get_split_list(lot); node; node = node->next)
00773 {
00774 Split *s = node->data;
00775 if (pcy->PolicyIsOpeningSplit(pcy, lot, s))
00776 {
00777 if (GAINS_STATUS_UNKNOWN == s->gains) xaccSplitDetermineGainStatus (s);
00778 if (s->gains & GAINS_STATUS_VDIRTY)
00779 {
00780
00781 split->gains |= GAINS_STATUS_VDIRTY;
00782 break;
00783 }
00784 }
00785 }
00786
00787
00788
00789 if ((FALSE == (split->gains & GAINS_STATUS_A_VDIRTY)) &&
00790 (split->gains_split) &&
00791 (FALSE == (split->gains_split->gains & GAINS_STATUS_A_VDIRTY)))
00792 {
00793 LEAVE ("split not dirty, returning");
00794 return;
00795 }
00796
00797
00798
00799 if (gnc_numeric_zero_p (split->amount)) return;
00800
00801
00802
00803
00804
00805
00806 gnc_lot_get_balance_before (lot, split, &lot_amount, &lot_value);
00807
00808 pcy->PolicyGetLotOpening (pcy, lot, &opening_amount, &opening_value,
00809 &opening_currency);
00810
00811
00812
00813 if (FALSE == gnc_commodity_equiv (currency, opening_currency))
00814 {
00815
00816
00817
00818
00819 LEAVE ("Can't compute gains, mismatched commodities!");
00820 return;
00821 }
00822
00823
00824
00825
00826
00827
00828 if (0 > gnc_numeric_compare (gnc_numeric_abs(lot_amount),
00829 gnc_numeric_abs(split->amount)))
00830 {
00831 GList *n;
00832 for (n = gnc_lot_get_split_list(lot); n; n = n->next)
00833 {
00834 Split *s = n->data;
00835 PINFO ("split amt=%s", gnc_num_dbg_to_string(s->amount));
00836 }
00837 PERR ("Malformed Lot \"%s\"! (too thin!) "
00838 "opening amt=%s split amt=%s baln=%s",
00839 gnc_lot_get_title (lot),
00840 gnc_num_dbg_to_string (lot_amount),
00841 gnc_num_dbg_to_string (split->amount),
00842 gnc_num_dbg_to_string (gnc_lot_get_balance(lot)));
00843 return;
00844 }
00845 if ( (gnc_numeric_negative_p(lot_amount) ||
00846 gnc_numeric_positive_p(split->amount)) &&
00847 (gnc_numeric_positive_p(lot_amount) ||
00848 gnc_numeric_negative_p(split->amount)))
00849 {
00850 GList *n;
00851 for (n = gnc_lot_get_split_list(lot); n; n = n->next)
00852 {
00853 Split *s = n->data;
00854 PINFO ("split amt=%s", gnc_num_dbg_to_string(s->amount));
00855 }
00856 PERR ("Malformed Lot \"%s\"! (too fat!) "
00857 "opening amt=%s split amt=%s baln=%s",
00858 gnc_lot_get_title (lot),
00859 gnc_num_dbg_to_string (lot_amount),
00860 gnc_num_dbg_to_string (split->amount),
00861 gnc_num_dbg_to_string (gnc_lot_get_balance(lot)));
00862 return;
00863 }
00864
00865
00866
00867
00868
00869
00870
00871
00872
00873 frac = gnc_numeric_div (split->amount, lot_amount,
00874 GNC_DENOM_AUTO,
00875 GNC_HOW_DENOM_REDUCE);
00876
00877 value = gnc_numeric_mul (frac, lot_value,
00878 gnc_numeric_denom(opening_value),
00879 GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
00880
00881 value = gnc_numeric_sub (value, split->value,
00882 GNC_DENOM_AUTO, GNC_HOW_DENOM_FIXED);
00883 PINFO ("Open amt=%s val=%s; split amt=%s val=%s; gains=%s\n",
00884 gnc_num_dbg_to_string (lot_amount),
00885 gnc_num_dbg_to_string (lot_value),
00886 gnc_num_dbg_to_string (split->amount),
00887 gnc_num_dbg_to_string (split->value),
00888 gnc_num_dbg_to_string (value));
00889 if (gnc_numeric_check (value))
00890 {
00891 PERR ("Numeric overflow during gains calculation\n"
00892 "Acct=%s Txn=%s\n"
00893 "\tOpen amt=%s val=%s\n\tsplit amt=%s val=%s\n\tgains=%s\n",
00894 xaccAccountGetName(split->acc),
00895 xaccTransGetDescription(split->parent),
00896 gnc_num_dbg_to_string (lot_amount),
00897 gnc_num_dbg_to_string (lot_value),
00898 gnc_num_dbg_to_string (split->amount),
00899 gnc_num_dbg_to_string (split->value),
00900 gnc_num_dbg_to_string (value));
00901 return;
00902 }
00903
00904
00905
00906
00907
00908
00909
00910 if (FALSE == gnc_numeric_zero_p (value))
00911 {
00912 Transaction *trans;
00913 Split *lot_split, *gain_split;
00914 Timespec ts;
00915 gboolean new_gain_split;
00916 gnc_numeric negvalue = gnc_numeric_neg (value);
00917
00918
00919
00920
00921
00922
00923 lot_split = split->gains_split;
00924
00925 if (NULL == lot_split)
00926 {
00927 Account *lot_acc = gnc_lot_get_account(lot);
00928 QofBook *book = qof_instance_get_book(lot_acc);
00929
00930 new_gain_split = TRUE;
00931
00932 lot_split = xaccMallocSplit (book);
00933 gain_split = xaccMallocSplit (book);
00934
00935
00936 if ((NULL == gain_acc) ||
00937 (FALSE == gnc_commodity_equiv (currency,
00938 xaccAccountGetCommodity(gain_acc))))
00939 {
00940 gain_acc = GetOrMakeGainAcct (lot_acc, currency);
00941 }
00942
00943 xaccAccountBeginEdit (gain_acc);
00944 xaccAccountInsertSplit (gain_acc, gain_split);
00945 xaccAccountCommitEdit (gain_acc);
00946
00947 xaccAccountBeginEdit (lot_acc);
00948 xaccAccountInsertSplit (lot_acc, lot_split);
00949 xaccAccountCommitEdit (lot_acc);
00950
00951 trans = xaccMallocTransaction (book);
00952
00953 xaccTransBeginEdit (trans);
00954 xaccTransSetCurrency (trans, currency);
00955 xaccTransSetDescription (trans, _("Realized Gain/Loss"));
00956
00957 xaccTransAppendSplit (trans, lot_split);
00958 xaccTransAppendSplit (trans, gain_split);
00959
00960 xaccSplitSetMemo (lot_split, _("Realized Gain/Loss"));
00961 xaccSplitSetMemo (gain_split, _("Realized Gain/Loss"));
00962
00963
00964
00965
00966
00967 kvp_frame_set_guid (split->inst.kvp_data, "gains-split",
00968 xaccSplitGetGUID (lot_split));
00969 kvp_frame_set_guid (lot_split->inst.kvp_data, "gains-source",
00970 xaccSplitGetGUID (split));
00971
00972 }
00973 else
00974 {
00975 trans = lot_split->parent;
00976 gain_split = xaccSplitGetOtherSplit (lot_split);
00977
00978
00979
00980 if (!gain_split)
00981 {
00982 new_gain_split = FALSE;
00983 }
00984
00985
00986
00987
00988 else if (split->gains_split == lot_split &&
00989 lot_split->gains_split == split &&
00990 gain_split->gains_split == split &&
00991 gnc_numeric_equal (xaccSplitGetValue (lot_split), value) &&
00992 gnc_numeric_zero_p (xaccSplitGetAmount (lot_split)) &&
00993 gnc_numeric_equal (xaccSplitGetValue (gain_split), negvalue) &&
00994 gnc_numeric_equal (xaccSplitGetAmount (gain_split), negvalue))
00995 {
00996 new_gain_split = FALSE;
00997 }
00998 else
00999 {
01000 new_gain_split = TRUE;
01001 xaccTransBeginEdit (trans);
01002
01003
01004
01005 if (FALSE == gnc_commodity_equiv(currency, trans->common_currency))
01006 {
01007 PWARN ("Resetting the transaction currency!");
01008 xaccTransSetCurrency (trans, currency);
01009 }
01010 }
01011 }
01012
01013 if (new_gain_split)
01014 {
01015
01016 ts = xaccTransRetDatePostedTS (split->parent);
01017 xaccTransSetDatePostedTS (trans, &ts);
01018 xaccTransSetDateEnteredSecs (trans, time(0));
01019
01020 xaccSplitSetAmount (lot_split, zero);
01021 xaccSplitSetValue (lot_split, value);
01022
01023 xaccSplitSetAmount (gain_split, negvalue);
01024 xaccSplitSetValue (gain_split, negvalue);
01025
01026
01027 split->gains = GAINS_STATUS_CLEAN;
01028 split->gains_split = lot_split;
01029 lot_split->gains = GAINS_STATUS_GAINS;
01030 lot_split->gains_split = split;
01031 gain_split->gains = GAINS_STATUS_GAINS;
01032 gain_split->gains_split = split;
01033
01034
01035
01036 gnc_lot_add_split (lot, lot_split);
01037
01038 xaccTransCommitEdit (trans);
01039 }
01040 }
01041 LEAVE ("(lot=%s)", gnc_lot_get_title(lot));
01042 }
01043
01044
01045
01046 gnc_numeric
01047 xaccSplitGetCapGains(Split * split)
01048 {
01049 if (!split) return gnc_numeric_zero();
01050 ENTER("(split=%p)", split);
01051
01052 if (GAINS_STATUS_UNKNOWN == split->gains)
01053 xaccSplitDetermineGainStatus(split);
01054 if ((split->gains & GAINS_STATUS_A_VDIRTY) ||
01055 (split->gains_split &&
01056 (split->gains_split->gains & GAINS_STATUS_A_VDIRTY)))
01057 {
01058 xaccSplitComputeCapGains (split, NULL);
01059 }
01060
01061
01062
01063
01064 if (!(GAINS_STATUS_GAINS & split->gains))
01065 {
01066
01067 split = split->gains_split;
01068 }
01069
01070 LEAVE("(split=%p)", split);
01071 if (!split) return gnc_numeric_zero();
01072
01073 return split->value;
01074 }
01075
01076
01077
01078 void
01079 xaccLotComputeCapGains (GNCLot *lot, Account *gain_acc)
01080 {
01081 SplitList *node;
01082 GNCPolicy *pcy;
01083 gboolean is_dirty = FALSE;
01084
01085
01086
01087
01088
01089 ENTER("(lot=%p)", lot);
01090 pcy = gnc_account_get_policy(gnc_lot_get_account(lot));
01091 for (node = gnc_lot_get_split_list(lot); node; node = node->next)
01092 {
01093 Split *s = node->data;
01094 if (pcy->PolicyIsOpeningSplit(pcy, lot, s))
01095 {
01096 if (GAINS_STATUS_UNKNOWN == s->gains)
01097 xaccSplitDetermineGainStatus(s);
01098 if (s->gains & GAINS_STATUS_VDIRTY)
01099 {
01100 is_dirty = TRUE;
01101 s->gains &= ~GAINS_STATUS_VDIRTY;
01102 }
01103 }
01104 }
01105
01106 if (is_dirty)
01107 {
01108 for (node = gnc_lot_get_split_list(lot); node; node = node->next)
01109 {
01110 Split *s = node->data;
01111 s->gains |= GAINS_STATUS_VDIRTY;
01112 }
01113 }
01114
01115 for (node = gnc_lot_get_split_list(lot); node; node = node->next)
01116 {
01117 Split *s = node->data;
01118 xaccSplitComputeCapGains (s, gain_acc);
01119 }
01120 LEAVE("(lot=%p)", lot);
01121 }
01122
01123