00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00033 #include "config.h"
00034
00035 #include <glib.h>
00036
00037 #include "qof.h"
00038 #include "Account.h"
00039 #include "AccountP.h"
00040 #include "Transaction.h"
00041 #include "TransactionP.h"
00042 #include "Scrub2.h"
00043 #include "ScrubP.h"
00044 #include "cap-gains.h"
00045 #include "gnc-engine.h"
00046 #include "gnc-lot.h"
00047 #include "gnc-lot-p.h"
00048 #include "policy-p.h"
00049
00050 static QofLogModule log_module = GNC_MOD_LOT;
00051
00052
00058 void
00059 xaccAccountAssignLots (Account *acc)
00060 {
00061 SplitList *splits, *node;
00062
00063 if (!acc) return;
00064
00065 ENTER ("acc=%s", xaccAccountGetName(acc));
00066 xaccAccountBeginEdit (acc);
00067
00068 restart_loop:
00069 splits = xaccAccountGetSplitList(acc);
00070 for (node=splits; node; node=node->next)
00071 {
00072 Split * split = node->data;
00073
00074
00075 if (split->lot) continue;
00076
00077
00078 if (gnc_numeric_zero_p (split->amount) &&
00079 xaccTransGetVoidStatus(split->parent)) continue;
00080
00081 if (xaccSplitAssign (split)) goto restart_loop;
00082 }
00083 xaccAccountCommitEdit (acc);
00084 LEAVE ("acc=%s", xaccAccountGetName(acc));
00085 }
00086
00087
00088
00096 void
00097 xaccLotFill (GNCLot *lot)
00098 {
00099 Account *acc;
00100 Split *split;
00101 GNCPolicy *pcy;
00102
00103 if (!lot) return;
00104 acc = lot->account;
00105 pcy = gnc_account_get_policy(acc);
00106
00107 ENTER ("(lot=%s, acc=%s)", gnc_lot_get_title(lot), xaccAccountGetName(acc));
00108
00109
00110 if (gnc_lot_is_closed (lot)) return;
00111
00112 split = pcy->PolicyGetSplit (pcy, lot);
00113 if (!split) return;
00114
00115
00116 if (gnc_numeric_zero_p(split->amount) &&
00117 xaccTransGetVoidStatus(split->parent)) return;
00118
00119 xaccAccountBeginEdit (acc);
00120
00121
00122
00123 while (1)
00124 {
00125 Split *subsplit;
00126
00127 subsplit = xaccSplitAssignToLot (split, lot);
00128 if (subsplit == split)
00129 {
00130 PERR ("Accounting Policy gave us a split that "
00131 "doesn't fit into this lot\n"
00132 "lot baln=%s, isclosed=%d, aplit amt=%s",
00133 gnc_num_dbg_to_string (gnc_lot_get_balance(lot)),
00134 gnc_lot_is_closed (lot),
00135 gnc_num_dbg_to_string (split->amount));
00136 break;
00137 }
00138
00139 if (gnc_lot_is_closed (lot)) break;
00140
00141 split = pcy->PolicyGetSplit (pcy, lot);
00142 if (!split) break;
00143 }
00144 xaccAccountCommitEdit (acc);
00145 LEAVE ("(lot=%s, acc=%s)", gnc_lot_get_title(lot), xaccAccountGetName(acc));
00146 }
00147
00148
00149
00150 void
00151 xaccLotScrubDoubleBalance (GNCLot *lot)
00152 {
00153 gnc_commodity *currency = NULL;
00154 SplitList *snode;
00155 GList *node;
00156 gnc_numeric zero = gnc_numeric_zero();
00157 gnc_numeric value = zero;
00158
00159 if (!lot) return;
00160
00161 ENTER ("lot=%s", kvp_frame_get_string (gnc_lot_get_slots (lot), "/title"));
00162
00163 for (snode = lot->splits; snode; snode=snode->next)
00164 {
00165 Split *s = snode->data;
00166 xaccSplitComputeCapGains (s, NULL);
00167 }
00168
00169
00170 if (FALSE == gnc_lot_is_closed (lot)) return;
00171
00172 for (snode = lot->splits; snode; snode=snode->next)
00173 {
00174 Split *s = snode->data;
00175 Transaction *trans = s->parent;
00176
00177
00178 if (NULL == currency)
00179 {
00180 currency = trans->common_currency;
00181 }
00182 if (FALSE == gnc_commodity_equiv (currency, trans->common_currency))
00183 {
00184
00185
00186 PWARN ("Lot with multiple currencies:\n"
00187 "\ttrans=%s curr=%s", xaccTransGetDescription(trans),
00188 gnc_commodity_get_fullname(trans->common_currency));
00189 break;
00190 }
00191
00192
00193 value = gnc_numeric_add (value, xaccSplitGetValue (s),
00194 GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT);
00195 PINFO ("Split=%p value=%s Accum Lot value=%s", s,
00196 gnc_num_dbg_to_string (s->value),
00197 gnc_num_dbg_to_string (value));
00198
00199 }
00200
00201 if (FALSE == gnc_numeric_equal (value, zero))
00202 {
00203
00204
00205
00206
00207
00208
00209 PERR ("Closed lot fails to double-balance !! lot value=%s",
00210 gnc_num_dbg_to_string (value));
00211 for (node=lot->splits; node; node=node->next)
00212 {
00213 Split *s = node->data;
00214 PERR ("s=%p amt=%s val=%s", s,
00215 gnc_num_dbg_to_string(s->amount),
00216 gnc_num_dbg_to_string(s->value));
00217 }
00218 }
00219
00220 LEAVE ("lot=%s", kvp_frame_get_string (gnc_lot_get_slots (lot), "/title"));
00221 }
00222
00223
00224
00225 static inline gboolean
00226 is_subsplit (Split *split)
00227 {
00228 KvpValue *kval;
00229
00230
00231 if (!split) return FALSE;
00232 g_return_val_if_fail (split->parent, FALSE);
00233
00234
00235 kval = kvp_frame_get_slot (split->inst.kvp_data, "lot-split");
00236 if (!kval) return FALSE;
00237
00238 return TRUE;
00239 }
00240
00241
00242
00243 void
00244 xaccScrubSubSplitPrice (Split *split, int maxmult, int maxamtscu)
00245 {
00246 gnc_numeric src_amt, src_val;
00247 SplitList *node;
00248
00249 if (FALSE == is_subsplit (split)) return;
00250
00251 ENTER (" ");
00252
00253 src_amt = xaccSplitGetAmount (split);
00254 src_val = xaccSplitGetValue (split);
00255
00256
00257
00258
00259 for (node=split->parent->splits; node; node=node->next)
00260 {
00261 Split *s = node->data;
00262 Transaction *txn = s->parent;
00263 gnc_numeric dst_amt, dst_val, target_val;
00264 gnc_numeric frac, delta;
00265 int scu;
00266
00267
00268 if (s == split) continue;
00269
00270 scu = gnc_commodity_get_fraction (txn->common_currency);
00271
00272 dst_amt = xaccSplitGetAmount (s);
00273 dst_val = xaccSplitGetValue (s);
00274 frac = gnc_numeric_div (dst_amt, src_amt,
00275 GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE);
00276 target_val = gnc_numeric_mul (frac, src_val,
00277 scu, GNC_HOW_DENOM_EXACT|GNC_HOW_RND_ROUND);
00278 if (gnc_numeric_check (target_val))
00279 {
00280 PERR ("Numeric overflow of value\n"
00281 "\tAcct=%s txn=%s\n"
00282 "\tdst_amt=%s src_val=%s src_amt=%s\n",
00283 xaccAccountGetName (s->acc),
00284 xaccTransGetDescription(txn),
00285 gnc_num_dbg_to_string(dst_amt),
00286 gnc_num_dbg_to_string(src_val),
00287 gnc_num_dbg_to_string(src_amt));
00288 continue;
00289 }
00290
00291
00292
00293
00294
00295
00296 delta = gnc_numeric_sub_fixed (target_val, dst_val);
00297 delta = gnc_numeric_abs (delta);
00298 if (maxmult * delta.num < delta.denom) continue;
00299
00300
00301 if ((-maxamtscu < dst_amt.num) && (dst_amt.num < maxamtscu)) continue;
00302
00303
00304 xaccTransBeginEdit (txn);
00305 xaccSplitSetValue (s, target_val);
00306 xaccTransCommitEdit (txn);
00307 }
00308 LEAVE (" ");
00309 }
00310
00311
00312
00313
00314
00315
00316
00317 static void
00318 remove_guids (Split *sa, Split *sb)
00319 {
00320 KvpFrame *ksub;
00321
00322
00323 ksub = (KvpFrame*)gnc_kvp_bag_find_by_guid (sa->inst.kvp_data, "lot-split",
00324 "peer_guid", qof_instance_get_guid(sb));
00325 if (ksub)
00326 {
00327 gnc_kvp_bag_remove_frame (sa->inst.kvp_data, "lot-split", ksub);
00328 kvp_frame_delete (ksub);
00329 }
00330
00331
00332 ksub = (KvpFrame*)gnc_kvp_bag_find_by_guid (sb->inst.kvp_data, "lot-split",
00333 "peer_guid", qof_instance_get_guid(sa));
00334 if (ksub)
00335 {
00336 gnc_kvp_bag_remove_frame (sb->inst.kvp_data, "lot-split", ksub);
00337 kvp_frame_delete (ksub);
00338 }
00339
00340
00341
00342 gnc_kvp_bag_merge (sa->inst.kvp_data, "lot-split",
00343 sb->inst.kvp_data, "lot-split");
00344 }
00345
00346
00347
00348
00349
00350 static void
00351 merge_splits (Split *sa, Split *sb)
00352 {
00353 Account *act;
00354 Transaction *txn;
00355 gnc_numeric amt, val;
00356
00357 act = xaccSplitGetAccount (sb);
00358 xaccAccountBeginEdit (act);
00359
00360 txn = sa->parent;
00361 xaccTransBeginEdit (txn);
00362
00363
00364 remove_guids (sa, sb);
00365
00366
00367 amt = xaccSplitGetAmount (sa);
00368 amt = gnc_numeric_add_fixed (amt, xaccSplitGetAmount (sb));
00369 xaccSplitSetAmount (sa, amt);
00370
00371 val = xaccSplitGetValue (sa);
00372 val = gnc_numeric_add_fixed (val, xaccSplitGetValue (sb));
00373 xaccSplitSetValue (sa, val);
00374
00375
00376
00377 xaccSplitSetReconcile (sa, NREC);
00378
00379
00380 if ((sb->gains_split) &&
00381 (sb->gains_split->gains & GAINS_STATUS_GAINS))
00382 {
00383 Transaction *t = sb->gains_split->parent;
00384 xaccTransBeginEdit (t);
00385 xaccTransDestroy (t);
00386 xaccTransCommitEdit (t);
00387 }
00388
00389
00390 xaccSplitDestroy(sb);
00391
00392 xaccTransCommitEdit (txn);
00393 xaccAccountCommitEdit (act);
00394 }
00395
00396 gboolean
00397 xaccScrubMergeSubSplits (Split *split)
00398 {
00399 gboolean rc = FALSE;
00400 Transaction *txn;
00401 SplitList *node;
00402 GNCLot *lot;
00403 const GUID *guid;
00404
00405 if (FALSE == is_subsplit (split)) return FALSE;
00406
00407 txn = split->parent;
00408 lot = xaccSplitGetLot (split);
00409
00410 ENTER ("(Lot=%s)", gnc_lot_get_title(lot));
00411 restart:
00412 for (node=txn->splits; node; node=node->next)
00413 {
00414 Split *s = node->data;
00415 if (xaccSplitGetLot (s) != lot) continue;
00416 if (s == split) continue;
00417 if (qof_instance_get_destroying(s)) continue;
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427 guid = qof_instance_get_guid(s);
00428 if (gnc_kvp_bag_find_by_guid (split->inst.kvp_data, "lot-split",
00429 "peer_guid", guid) == NULL)
00430 continue;
00431
00432 merge_splits (split, s);
00433 rc = TRUE;
00434 goto restart;
00435 }
00436 if (gnc_numeric_zero_p (split->amount))
00437 {
00438 PWARN ("Result of merge has zero amt!");
00439 }
00440 LEAVE (" splits merged=%d", rc);
00441 return rc;
00442 }
00443
00444 gboolean
00445 xaccScrubMergeTransSubSplits (Transaction *txn)
00446 {
00447 gboolean rc = FALSE;
00448 SplitList *node;
00449
00450 if (!txn) return FALSE;
00451
00452 ENTER (" ");
00453 restart:
00454 for (node=txn->splits; node; node=node->next)
00455 {
00456 Split *s = node->data;
00457 if (!xaccScrubMergeSubSplits(s)) continue;
00458
00459 rc = TRUE;
00460 goto restart;
00461 }
00462 LEAVE (" splits merged=%d", rc);
00463 return rc;
00464 }
00465
00466 gboolean
00467 xaccScrubMergeLotSubSplits (GNCLot *lot)
00468 {
00469 gboolean rc = FALSE;
00470 SplitList *node;
00471
00472 if (!lot) return FALSE;
00473
00474 ENTER (" ");
00475 restart:
00476 for (node=gnc_lot_get_split_list(lot); node; node=node->next)
00477 {
00478 Split *s = node->data;
00479 if (!xaccScrubMergeSubSplits(s)) continue;
00480
00481 rc = TRUE;
00482 goto restart;
00483 }
00484 LEAVE (" splits merged=%d", rc);
00485 return rc;
00486 }
00487
00488