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