It is typically expected that the scrub routines are run over newly imported data, as well as during data file input.
In some cases, it is entirely appropriate to invoke these routines from the GUI, to validate that the user input through the GUI is in a format that the system likes. This includes things like balancing individual transactions, or assigning splits to lots, so that capital gains can be computed.
Files | |
| file | Scrub.h |
| convert single-entry accounts to clean double-entry | |
| file | Scrub2.h |
| Utilities to Convert Stock Accounts to use Lots. | |
| file | Scrub3.h |
| Hiogh-Level API for imposing Lot constraints. | |
Double-Entry Scrubbing | |
| Convert single-entry accounts to clean double-entry Provides a set of functions and utilities for checking and repairing (formerly called 'scrubbing clean') single-entry accounts so that they can be promoted into self-consistent, clean double-entry accounts. Basically and additionally, this file collects all functions that turn old (deprecated) data structures into the current new data model.
The ScrubOrphans() methods search for transacations that contain splits that do not have a parent account. These "orphaned splits" are placed into an "orphan account" which the user will have to go into and clean up. Kind of like the unix "Lost+Found" directory for orphaned inodes. | |
| void | xaccTransScrubOrphans (Transaction *trans) |
| void | xaccAccountScrubOrphans (Account *acc) |
| void | xaccAccountTreeScrubOrphans (Account *acc) |
| void | xaccSplitScrub (Split *split) |
| void | xaccTransScrubSplits (Transaction *trans) |
| void | xaccAccountScrubSplits (Account *account) |
| void | xaccAccountTreeScrubSplits (Account *account) |
| void | xaccTransScrubImbalance (Transaction *trans, Account *root, Account *parent) |
| void | xaccAccountScrubImbalance (Account *acc) |
| void | xaccAccountTreeScrubImbalance (Account *acc) |
| void | xaccTransScrubCurrency (Transaction *trans) |
| void | xaccTransScrubCurrencyFromSplits (Transaction *trans) |
| void | xaccAccountScrubCommodity (Account *account) |
| void | xaccAccountTreeScrubCommodities (Account *acc) |
| void | xaccAccountTreeScrubQuoteSources (Account *root, gnc_commodity_table *table) |
| void | xaccAccountScrubKvp (Account *account) |
Lot Management Routines | |
| Provides the low-level API for checking and repairing ('scrubbing clean') the usage of Lots and lot balances in stock and commodity accounts. Broken lots are repaired using a first-in, first-out (FIFO) accounting schedule.
This is a 'low-level' API in the sense that each routine accomplishes only one particular task needed to clean up a Lot. To clean up a Lot as a whole, you almost certainly want to use one of the high-level API routines from the Scrub3.h file. | |
| void | xaccAccountAssignLots (Account *acc) |
| void | xaccLotFill (GNCLot *lot) |
| void | xaccLotScrubDoubleBalance (GNCLot *lot) |
| void | xaccScrubSubSplitPrice (Split *split, int maxmult, int maxamtscu) |
| gboolean | xaccScrubMergeSubSplits (Split *split) |
| gboolean | xaccScrubMergeTransSubSplits (Transaction *txn) |
| gboolean | xaccScrubMergeLotSubSplits (GNCLot *lot) |
High-Level Lot Constraint | |
| Provides the high-level API for checking and repairing ('scrubbing clean') the usage of Lots and Cap Gains transactions in stock and commodity accounts. | |
| gboolean | xaccScrubLot (GNCLot *lot) |
| void | xaccAccountScrubLots (Account *acc) |
| void | xaccAccountTreeScrubLots (Account *acc) |
| void xaccAccountAssignLots | ( | Account * | acc | ) |
Loop over all splits, and make sure that every split belongs to some lot. If a split does not belong to any lots, poke it into one.
Definition at line 59 of file Scrub2.c.
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 /* If already in lot, then no-op */ 00075 if (split->lot) continue; 00076 00077 /* Skip voided transactions */ 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 }
| void xaccAccountScrubCommodity | ( | Account * | account | ) |
The xaccAccountScrubCommodity method fixed accounts without a commodity by using the old account currency and security.
Definition at line 674 of file Scrub.c.
00675 { 00676 gnc_commodity *commodity; 00677 00678 if (!account) return; 00679 if (xaccAccountGetType(account) == ACCT_TYPE_ROOT) return; 00680 00681 commodity = xaccAccountGetCommodity (account); 00682 if (commodity) return; 00683 00684 /* Use the 'obsolete' routines to try to figure out what the 00685 * account commodity should have been. */ 00686 commodity = DxaccAccountGetSecurity (account); 00687 if (commodity) 00688 { 00689 xaccAccountSetCommodity (account, commodity); 00690 return; 00691 } 00692 00693 commodity = DxaccAccountGetCurrency (account); 00694 if (commodity) 00695 { 00696 xaccAccountSetCommodity (account, commodity); 00697 return; 00698 } 00699 00700 PERR ("Account \"%s\" does not have a commodity!", 00701 xaccAccountGetName(account)); 00702 }
| void xaccAccountScrubLots | ( | Account * | acc | ) |
The xaccAccountScrubLots() routine makes sure that every split in the account is assigned to a lot, and that then, every lot is self-consistent (by calling xaccScrubLot() on each lot).
This routine is the primary routine for ensuring that the lot structure, and the cap-gains for an account are in good order.
Most GUI routines will want to use one of these xacc[*]ScrubLots() routines, instead of the various component routines, since it will usually makes sense to work only with these high-level routines.
Definition at line 159 of file Scrub3.c.
00160 { 00161 LotList *lots, *node; 00162 if (!acc) return; 00163 if (FALSE == xaccAccountHasTrades (acc)) return; 00164 00165 ENTER ("(acc=%s)", xaccAccountGetName(acc)); 00166 xaccAccountBeginEdit(acc); 00167 xaccAccountAssignLots (acc); 00168 00169 lots = xaccAccountGetLotList(acc); 00170 for (node = lots; node; node=node->next) 00171 { 00172 GNCLot *lot = node->data; 00173 xaccScrubLot (lot); 00174 } 00175 g_list_free(lots); 00176 xaccAccountCommitEdit(acc); 00177 LEAVE ("(acc=%s)", xaccAccountGetName(acc)); 00178 }
| void xaccAccountScrubOrphans | ( | Account * | acc | ) |
The xaccAccountScrubOrphans() method performs this scrub only for the indicated account, and not for any of its children.
Definition at line 96 of file Scrub.c.
00097 { 00098 GList *node; 00099 const char *str; 00100 00101 if (!acc) return; 00102 00103 str = xaccAccountGetName (acc); 00104 str = str ? str : "(null)"; 00105 PINFO ("Looking for orphans in account %s \n", str); 00106 00107 for (node = xaccAccountGetSplitList(acc); node; node = node->next) 00108 { 00109 Split *split = node->data; 00110 00111 TransScrubOrphansFast (xaccSplitGetParent (split), 00112 gnc_account_get_root (acc)); 00113 } 00114 }
| void xaccAccountTreeScrubCommodities | ( | Account * | acc | ) |
The xaccAccountTreeScrubCommodities will scrub the currency/commodity of all accounts & transactions in the specified account or any child account.
Definition at line 732 of file Scrub.c.
00733 { 00734 if (!acc) return; 00735 00736 xaccAccountTreeForEachTransaction (acc, scrub_trans_currency_helper, NULL); 00737 00738 scrub_account_commodity_helper (acc, NULL); 00739 gnc_account_foreach_descendant (acc, scrub_account_commodity_helper, NULL); 00740 }
| void xaccAccountTreeScrubOrphans | ( | Account * | acc | ) |
The xaccAccountTreeScrubOrphans() method performs this scrub for the indicated account and its children.
Definition at line 62 of file Scrub.c.
00063 { 00064 if (!acc) return; 00065 00066 xaccAccountScrubOrphans (acc); 00067 gnc_account_foreach_descendant(acc, 00068 (AccountCb)xaccAccountScrubOrphans, NULL); 00069 }
| void xaccAccountTreeScrubQuoteSources | ( | Account * | root, | |
| gnc_commodity_table * | table | |||
| ) |
This routine will migrate the information about price quote sources from the account data structures to the commodity data structures. It first checks to see if this is necessary since, for the time being, the quote information will still be written out as part of the account. Just in case anyone needs to fall back from CVS to a production version of code.
| acc | A pointer to the root account containing all accounts in the current book. | |
| table | A pointer to the commodity table for the current book. |
Definition at line 788 of file Scrub.c.
00789 { 00790 gboolean new_style = FALSE; 00791 ENTER(" "); 00792 00793 if (!root || !table) { 00794 LEAVE("Oops"); 00795 return; 00796 } 00797 00798 gnc_commodity_table_foreach_commodity (table, check_quote_source, &new_style); 00799 00800 move_quote_source(root, GINT_TO_POINTER(new_style)); 00801 gnc_account_foreach_descendant (root, move_quote_source, 00802 GINT_TO_POINTER(new_style)); 00803 LEAVE("Migration done"); 00804 }
| void xaccLotFill | ( | GNCLot * | lot | ) |
The xaccLotFill() routine attempts to assign splits to the indicated lot until the lot balance goes to zero, or until there are no suitable (i.e. unassigned) splits left in the account. It uses the default accounting policy to choose the splits to fill out the lot.
Definition at line 97 of file Scrub2.c.
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 /* If balance already zero, we have nothing to do. */ 00110 if (gnc_lot_is_closed (lot)) return; 00111 00112 split = pcy->PolicyGetSplit (pcy, lot); 00113 if (!split) return; /* Handle the common case */ 00114 00115 /* Reject voided transactions */ 00116 if (gnc_numeric_zero_p(split->amount) && 00117 xaccTransGetVoidStatus(split->parent)) return; 00118 00119 xaccAccountBeginEdit (acc); 00120 00121 /* Loop until we've filled up the lot, (i.e. till the 00122 * balance goes to zero) or there are no splits left. */ 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 }
| void xaccLotScrubDoubleBalance | ( | GNCLot * | lot | ) |
The xaccLotScrubDoubleBalance() routine examines the indicated lot. If it is open, it does nothing. If it is closed, it then verifies that the lot is 'double balanced'. By 'double balance', we mean that both the sum of the split amounts is zero, and that the sum of the split values is zero. If the lot is closed and the sum of the values is not zero, the lot is considered to have a 'realized gain or loss' that hadn't been correctly handled. This routine then creates a balancing transaction to so as to record the realized gain/loss, adds it to the lot, and adds it to a gain/loss account. If there is no default gain/loss account, it creates one.
Definition at line 151 of file Scrub2.c.
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 /* We double-check only closed lots */ 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 /* Check to make sure all splits in the lot have a common currency */ 00178 if (NULL == currency) 00179 { 00180 currency = trans->common_currency; 00181 } 00182 if (FALSE == gnc_commodity_equiv (currency, trans->common_currency)) 00183 { 00184 /* This lot has mixed currencies. Can't double-balance. 00185 * Silently punt */ 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 /* Now, total up the values */ 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 /* Unhandled error condition. Not sure what to do here, 00204 * Since the ComputeCapGains should have gotten it right. 00205 * I suppose there might be small rounding errors, a penny or two, 00206 * the ideal thing would to figure out why there's a rounding 00207 * error, and fix that. 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 }
| gboolean xaccScrubLot | ( | GNCLot * | lot | ) |
The xaccScrubLot() routine makes sure that the indicated lot is self-consistent and properly balanced, and fixes it if its not. This is an important routine to call if the amount of any split in the lot is changed. That's because (obviously) changing split values is gaurenteed to throw off lot balances. This routine may end up closing the lot, or at least trying to. It will also cause cap gains to be recomputed.
Scrubbing the lot may cause subsplits to be merged together, i.e. for splits to be deleted. This routine returns true if any splits were deleted.
Definition at line 85 of file Scrub3.c.
00086 { 00087 gboolean splits_deleted = FALSE; 00088 gnc_numeric lot_baln; 00089 gboolean opening_baln_is_pos, lot_baln_is_pos; 00090 Account *acc; 00091 GNCPolicy *pcy; 00092 00093 if (!lot) return FALSE; 00094 ENTER ("(lot=%p) %s", lot, gnc_lot_get_title(lot)); 00095 00096 acc = gnc_lot_get_account (lot); 00097 pcy = gnc_account_get_policy(acc); 00098 xaccAccountBeginEdit(acc); 00099 xaccScrubMergeLotSubSplits (lot); 00100 00101 /* If the lot balance is zero, we don't need to rebalance */ 00102 lot_baln = gnc_lot_get_balance (lot); 00103 PINFO ("lot baln=%s for %s", gnc_num_dbg_to_string (lot_baln), 00104 gnc_lot_get_title(lot)); 00105 if (! gnc_numeric_zero_p (lot_baln)) 00106 { 00107 SplitList *node; 00108 gnc_numeric opening_baln; 00109 00110 /* Get the opening balance for this lot */ 00111 pcy->PolicyGetLotOpening (pcy, lot, &opening_baln, NULL, NULL); 00112 PINFO ("lot opener baln=%s", gnc_num_dbg_to_string (opening_baln)); 00113 00114 /* If the lot is fat, give the boot to all the non-opening 00115 * splits, and refill it */ 00116 opening_baln_is_pos = gnc_numeric_positive_p(opening_baln); 00117 lot_baln_is_pos = gnc_numeric_positive_p(lot_baln); 00118 if ((opening_baln_is_pos || lot_baln_is_pos) && 00119 ((!opening_baln_is_pos) || (!lot_baln_is_pos))) 00120 { 00121 rethin: 00122 for (node=gnc_lot_get_split_list(lot); node; node=node->next) 00123 { 00124 Split *s = node->data; 00125 if (pcy->PolicyIsOpeningSplit (pcy, lot, s)) continue; 00126 gnc_lot_remove_split (lot, s); 00127 goto rethin; 00128 } 00129 } 00130 00131 /* At this point the lot is thin, so try to fill it */ 00132 xaccLotFill (lot); 00133 00134 /* Make sure there are no subsplits. */ 00135 splits_deleted = xaccScrubMergeLotSubSplits (lot); 00136 } 00137 00138 /* Now re-compute cap gains, and then double-check that. 00139 * But we only compute cap-gains if gains are possible; 00140 * that is if the lot commodity is not the same as the 00141 * currency. That is, one can't possibly have gains 00142 * selling dollars for dollars. The business modules 00143 * use lots with lot commodity == lot currency. 00144 */ 00145 if (gains_possible (lot)) 00146 { 00147 xaccLotComputeCapGains (lot, NULL); 00148 xaccLotScrubDoubleBalance (lot); 00149 } 00150 xaccAccountCommitEdit(acc); 00151 00152 LEAVE ("(lot=%s, deleted=%d)", gnc_lot_get_title(lot), splits_deleted); 00153 return splits_deleted; 00154 }
| gboolean xaccScrubMergeSubSplits | ( | Split * | split | ) |
The xaccScrubMergeSubSplits() routine will merge together all of the splits that were at one time split off from this split, but are no longer needed to be kept separate. Splits might be split up if they need to be divided over multiple lots; they can be merged back together if the lots change. In particular, two sub-splits may be merged if they are in the same lot, or in no lot. Note that, by definition, all subsplits belong to the same transaction.
The routine returns TRUE if a merger was performed, else it returns FALSE.
The xaccScrubMergeTransSubSplits() routine does the same, except that it does it for all of the splits in the transaction. The xaccScrubMergeLotSubSplits() routine does the same, except that it does it for all of the splits in the lot.
Definition at line 397 of file Scrub2.c.
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 /* OK, this split is in the same lot (and thus same account) 00420 * as the indicated split. Make sure it is really a subsplit 00421 * of the split we started with. It's possible to have two 00422 * splits in the same lot and transaction that are not subsplits 00423 * of each other, the test-period test suite does this, for 00424 * example. Only worry about adjacent sub-splits. By 00425 * repeatedly merging adjacent subsplits, we'll get the non- 00426 * adjacent ones too. */ 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 }
| void xaccScrubSubSplitPrice | ( | Split * | split, | |
| int | maxmult, | |||
| int | maxamtscu | |||
| ) |
If a split has been pulled apart to make it fit into two (or more) lots, then it becomes theoretically possible for each subsplit to have a distinct price. But this would be wrong: each subsplit should have the same price, within rounding errors. This routine will examine the indicated split for sub-splits, and adjust the value of each so that they all have the same price.
There is a bit of a problem with the interpretation of 'rounding errors' because there are pathological corner cases of small amounts. So this routine is loose, hopefully loose enough so that the user can manually fine tune without having this routine clobber thier work.
This routine ignores price differences smaller than 1/maxmult. This routine ignores price differences when the split with a crazy price involes only a small amount: specifically, an amount that is less than maxamtscu/amount.denom.
Reasonable/recommended values might be maxmult=3, maxamtscu = 2.
Definition at line 244 of file Scrub2.c.
00245 { 00246 gnc_numeric src_amt, src_val; 00247 SplitList *node; 00248 00249 if (FALSE == is_subsplit (split)) return; 00250 00251 ENTER (" "); 00252 /* Get 'price' of the indicated split */ 00253 src_amt = xaccSplitGetAmount (split); 00254 src_val = xaccSplitGetValue (split); 00255 00256 /* Loop over splits, adjust each so that it has the same 00257 * ratio (i.e. price). Change the value to get things 00258 * right; do not change the amount */ 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 /* Skip the reference split */ 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 /* If the required price changes are 'small', do nothing. 00292 * That is a case that the user will have to deal with 00293 * manually. This routine is really intended only for 00294 * a gross level of synchronization. 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 /* If the amount is small, pass on that too */ 00301 if ((-maxamtscu < dst_amt.num) && (dst_amt.num < maxamtscu)) continue; 00302 00303 /* Make the actual adjustment */ 00304 xaccTransBeginEdit (txn); 00305 xaccSplitSetValue (s, target_val); 00306 xaccTransCommitEdit (txn); 00307 } 00308 LEAVE (" "); 00309 }
| void xaccSplitScrub | ( | Split * | split | ) |
The xaccSplitScrub method ensures that if this split has the same commodity and currency, then it will have the same amount and value. If the commoidty is the currency, the split->amount is set to the split value. In addition, if this split is an orphan, that is fixed first. If the split account doesn't have a commodity declared, an attempt is made to fix that first.
Definition at line 167 of file Scrub.c.
00168 { 00169 Account *account; 00170 Transaction *trans; 00171 gnc_numeric value, amount; 00172 gnc_commodity *currency, *acc_commodity; 00173 int scu; 00174 00175 if (!split) return; 00176 ENTER ("(split=%p)", split); 00177 00178 trans = xaccSplitGetParent (split); 00179 if (!trans) { 00180 LEAVE("no trans"); 00181 return; 00182 } 00183 00184 account = xaccSplitGetAccount (split); 00185 00186 /* If there's no account, this split is an orphan. 00187 * We need to fix that first, before proceeding. 00188 */ 00189 if (!account) 00190 { 00191 xaccTransScrubOrphans (trans); 00192 account = xaccSplitGetAccount (split); 00193 } 00194 00195 /* Grrr... the register gnc_split_register_load() line 203 of 00196 * src/register/ledger-core/split-register-load.c will create 00197 * free-floating bogus transactions. Ignore these for now ... 00198 */ 00199 if (!account) 00200 { 00201 PINFO ("Free Floating Transaction!"); 00202 LEAVE ("no account"); 00203 return; 00204 } 00205 00206 /* Split amounts and values should be valid numbers */ 00207 value = xaccSplitGetValue (split); 00208 if (gnc_numeric_check (value)) 00209 { 00210 value = gnc_numeric_zero(); 00211 xaccSplitSetValue (split, value); 00212 } 00213 00214 amount = xaccSplitGetAmount (split); 00215 if (gnc_numeric_check (amount)) 00216 { 00217 amount = gnc_numeric_zero(); 00218 xaccSplitSetAmount (split, amount); 00219 } 00220 00221 currency = xaccTransGetCurrency (trans); 00222 00223 /* If the account doesn't have a commodity, 00224 * we should attempt to fix that first. 00225 */ 00226 acc_commodity = xaccAccountGetCommodity(account); 00227 if (!acc_commodity) 00228 { 00229 xaccAccountScrubCommodity (account); 00230 } 00231 if (!acc_commodity || !gnc_commodity_equiv(acc_commodity, currency)) 00232 { 00233 LEAVE ("(split=%p) inequiv currency", split); 00234 return; 00235 } 00236 00237 scu = MIN (xaccAccountGetCommoditySCU (account), 00238 gnc_commodity_get_fraction (currency)); 00239 00240 if (gnc_numeric_same (amount, value, scu, GNC_HOW_RND_ROUND)) 00241 { 00242 LEAVE("(split=%p) different values", split); 00243 return; 00244 } 00245 00246 /* 00247 * This will be hit every time you answer yes to the dialog "The 00248 * current transaction has changed. Would you like to record it. 00249 */ 00250 PINFO ("Adjusted split with mismatched values, desc=\"%s\" memo=\"%s\"" 00251 " old amount %s %s, new amount %s", 00252 trans->description, split->memo, 00253 gnc_num_dbg_to_string (xaccSplitGetAmount(split)), 00254 gnc_commodity_get_mnemonic (currency), 00255 gnc_num_dbg_to_string (xaccSplitGetValue(split))); 00256 00257 xaccTransBeginEdit (trans); 00258 xaccSplitSetAmount (split, value); 00259 xaccTransCommitEdit (trans); 00260 LEAVE ("(split=%p)", split); 00261 }
| void xaccTransScrubCurrency | ( | Transaction * | trans | ) |
The xaccTransScrubCurrency method fixes transactions without a common_currency by using the old account currency and security fields of the parent accounts of the transaction's splits.
Definition at line 565 of file Scrub.c.
00566 { 00567 SplitList *node; 00568 gnc_commodity *currency; 00569 00570 if (!trans) return; 00571 00572 /* If there are any orphaned splits in a transaction, then the 00573 * this routine will fail. Therefore, we want to make sure that 00574 * there are no orphans (splits without parent account). 00575 */ 00576 xaccTransScrubOrphans (trans); 00577 00578 currency = xaccTransGetCurrency (trans); 00579 if (currency) return; 00580 00581 currency = xaccTransFindOldCommonCurrency (trans, qof_instance_get_book(trans)); 00582 if (currency) 00583 { 00584 xaccTransBeginEdit (trans); 00585 xaccTransSetCurrency (trans, currency); 00586 xaccTransCommitEdit (trans); 00587 } 00588 else 00589 { 00590 if (NULL == trans->splits) 00591 { 00592 PWARN ("Transaction \"%s\" has no splits in it!", trans->description); 00593 } 00594 else 00595 { 00596 SplitList *node; 00597 char guid_str[GUID_ENCODING_LENGTH+1]; 00598 guid_to_string_buff(xaccTransGetGUID(trans), guid_str); 00599 PWARN ("no common transaction currency found for trans=\"%s\" (%s)", 00600 trans->description, guid_str); 00601 00602 for (node=trans->splits; node; node=node->next) 00603 { 00604 Split *split = node->data; 00605 if (NULL == split->acc) 00606 { 00607 PWARN (" split=\"%s\" is not in any account!", split->memo); 00608 } 00609 else 00610 { 00611 PWARN (" split=\"%s\" account=\"%s\" commodity=\"%s\"", 00612 split->memo, xaccAccountGetName(split->acc), 00613 gnc_commodity_get_mnemonic(xaccAccountGetCommodity(split->acc))); 00614 } 00615 } 00616 } 00617 } 00618 00619 for (node=trans->splits; node; node=node->next) 00620 { 00621 Split *sp = node->data; 00622 00623 if (!gnc_numeric_equal(xaccSplitGetAmount (sp), 00624 xaccSplitGetValue (sp))) 00625 { 00626 gnc_commodity *acc_currency; 00627 00628 acc_currency = sp->acc ? xaccAccountGetCommodity(sp->acc) : NULL; 00629 if (acc_currency == currency) 00630 { 00631 /* This Split needs fixing: The transaction-currency equals 00632 * the account-currency/commodity, but the amount/values are 00633 * inequal i.e. they still correspond to the security 00634 * (amount) and the currency (value). In the new model, the 00635 * value is the amount in the account-commodity -- so it 00636 * needs to be set to equal the amount (since the 00637 * account-currency doesn't exist anymore). 00638 * 00639 * Note: Nevertheless we lose some information here. Namely, 00640 * the information that the 'amount' in 'account-old-security' 00641 * was worth 'value' in 'account-old-currency'. Maybe it would 00642 * be better to store that information in the price database? 00643 * But then, for old currency transactions there is still the 00644 * 'other' transaction, which is going to keep that 00645 * information. So I don't bother with that here. -- cstim, 00646 * 2002/11/20. */ 00647 00648 PWARN ("Adjusted split with mismatched values, desc=\"%s\" memo=\"%s\"" 00649 " old amount %s %s, new amount %s", 00650 trans->description, sp->memo, 00651 gnc_num_dbg_to_string (xaccSplitGetAmount(sp)), 00652 gnc_commodity_get_mnemonic (currency), 00653 gnc_num_dbg_to_string (xaccSplitGetValue(sp))); 00654 xaccTransBeginEdit (trans); 00655 xaccSplitSetAmount (sp, xaccSplitGetValue(sp)); 00656 xaccTransCommitEdit (trans); 00657 } 00658 /*else 00659 { 00660 PINFO ("Ok: Split '%s' Amount %s %s, value %s %s", 00661 xaccSplitGetMemo (sp), 00662 gnc_num_dbg_to_string (amount), 00663 gnc_commodity_get_mnemonic (currency), 00664 gnc_num_dbg_to_string (value), 00665 gnc_commodity_get_mnemonic (acc_currency)); 00666 }*/ 00667 } 00668 } 00669 }
| void xaccTransScrubCurrencyFromSplits | ( | Transaction * | trans | ) |
The xaccTransScrubCurrencyFromSplits method fixes transactions where the currency doesn't match the currency used in the splits in the transaction. If all splits where the amount equals the value and where the commodity is a currency have the same currency, it sets the transaction's currency to that if it is anything else. If the splits don't match that description the transaction currency is not changed.
Definition at line 297 of file Scrub.c.
00298 { 00299 GList *node; 00300 gnc_commodity *common_currency = NULL; 00301 00302 if (!trans) return; 00303 00304 for (node = xaccTransGetSplitList (trans); node; node = node->next) { 00305 Split *split = node->data; 00306 00307 if (!xaccTransStillHasSplit(trans, split)) continue; 00308 if (gnc_numeric_equal(xaccSplitGetAmount (split), 00309 xaccSplitGetValue (split))) { 00310 00311 Account *s_account = xaccSplitGetAccount (split); 00312 gnc_commodity *s_commodity = xaccAccountGetCommodity (s_account); 00313 00314 if (s_commodity) { 00315 if (gnc_commodity_is_currency(s_commodity)) { 00316 /* Found a split where the amount is the same as the value and 00317 the commodity is a currency. If all splits in the transaction 00318 that fit this description are in the same currency then the 00319 transaction should be in that currency too. */ 00320 00321 if (common_currency == NULL) 00322 /* First one we've found, save the currency */ 00323 common_currency = s_commodity; 00324 else if ( !gnc_commodity_equiv (common_currency, s_commodity)) { 00325 /* Splits are inconsistent, more than one has a value equal to 00326 the amount, but they aren't all in the same currency. */ 00327 common_currency = NULL; 00328 break; 00329 } 00330 } 00331 } 00332 } 00333 } 00334 00335 if (common_currency && 00336 !gnc_commodity_equiv (common_currency, xaccTransGetCurrency (trans))) { 00337 00338 /* Found a common currency for the splits, and the transaction is not 00339 in that currency */ 00340 gboolean trans_was_open; 00341 00342 PINFO ("transaction in wrong currency"); 00343 00344 trans_was_open = xaccTransIsOpen (trans); 00345 00346 if (!trans_was_open) 00347 xaccTransBeginEdit (trans); 00348 00349 xaccTransSetCurrency (trans, common_currency); 00350 00351 if (!trans_was_open) 00352 xaccTransCommitEdit (trans); 00353 } 00354 }
| void xaccTransScrubImbalance | ( | Transaction * | trans, | |
| Account * | root, | |||
| Account * | parent | |||
| ) |
The xaccScrubImbalance() method searches for transactions that do not balance to zero. If any such transactions are found, a split is created to offset this amount and is added to an "imbalance" account.
Definition at line 357 of file Scrub.c.
00359 { 00360 Split *balance_split = NULL; 00361 gnc_numeric imbalance; 00362 00363 if (!trans) return; 00364 00365 ENTER ("()"); 00366 00367 /* Must look for orphan splits even if there is no imbalance. */ 00368 xaccTransScrubSplits (trans); 00369 00370 /* If the transaction is balanced, nothing more to do */ 00371 imbalance = xaccTransGetImbalance (trans); 00372 if (gnc_numeric_zero_p (imbalance)) { 00373 LEAVE("zero imbalance"); 00374 return; 00375 } 00376 00377 if (!account) 00378 { 00379 if (!root) 00380 { 00381 root = gnc_book_get_root_account (xaccTransGetBook (trans)); 00382 if (NULL == root) 00383 { 00384 /* This can't occur, things should be in books */ 00385 PERR ("Bad data corruption, no root account in book"); 00386 LEAVE(""); 00387 return; 00388 } 00389 } 00390 account = xaccScrubUtilityGetOrMakeAccount (root, 00391 trans->common_currency, _("Imbalance")); 00392 if (!account) { 00393 PERR ("Can't get balancing account"); 00394 LEAVE(""); 00395 return; 00396 } 00397 } 00398 00399 balance_split = xaccTransFindSplitByAccount(trans, account); 00400 00401 /* Put split into account before setting split value */ 00402 if (!balance_split) 00403 { 00404 balance_split = xaccMallocSplit (qof_instance_get_book(trans)); 00405 00406 xaccTransBeginEdit (trans); 00407 xaccSplitSetParent(balance_split, trans); 00408 xaccSplitSetAccount(balance_split, account); 00409 xaccTransCommitEdit (trans); 00410 } 00411 00412 PINFO ("unbalanced transaction"); 00413 00414 { 00415 const gnc_commodity *currency; 00416 const gnc_commodity *commodity; 00417 gnc_numeric old_value, new_value; 00418 00419 xaccTransBeginEdit (trans); 00420 00421 currency = xaccTransGetCurrency (trans); 00422 old_value = xaccSplitGetValue (balance_split); 00423 00424 /* Note: We have to round for the commodity's fraction, NOT any 00425 * already existing denominator (bug #104343), because either one 00426 * of the denominators might already be reduced. */ 00427 new_value = gnc_numeric_sub (old_value, imbalance, 00428 gnc_commodity_get_fraction(currency), 00429 GNC_HOW_RND_ROUND); 00430 00431 xaccSplitSetValue (balance_split, new_value); 00432 00433 commodity = xaccAccountGetCommodity (account); 00434 if (gnc_commodity_equiv (currency, commodity)) 00435 { 00436 xaccSplitSetAmount (balance_split, new_value); 00437 } 00438 00439 xaccSplitScrub (balance_split); 00440 xaccTransCommitEdit (trans); 00441 } 00442 LEAVE ("()"); 00443 }
| void xaccTransScrubOrphans | ( | Transaction * | trans | ) |
The xaccTransScrubOrphans() method scrubs only the splits in the given transaction.
Definition at line 118 of file Scrub.c.
00119 { 00120 SplitList *node; 00121 QofBook *book = NULL; 00122 Account *root = NULL; 00123 for (node = trans->splits; node; node = node->next) 00124 { 00125 Split *split = node->data; 00126 00127 if (split->acc) 00128 { 00129 TransScrubOrphansFast (trans, gnc_account_get_root(split->acc)); 00130 return; 00131 } 00132 } 00133 00134 /* If we got to here, then *none* of the splits belonged to an 00135 * account. Not a happy situation. We should dig an account 00136 * out of the book the transaction belongs to. 00137 * XXX we should probably *always* to this, instead of the above loop! 00138 */ 00139 PINFO ("Free Floating Transaction!"); 00140 book = xaccTransGetBook (trans); 00141 root = gnc_book_get_root_account (book); 00142 TransScrubOrphansFast (trans, root); 00143 }
| void xaccTransScrubSplits | ( | Transaction * | trans | ) |
The xacc*ScrubSplits() calls xaccSplitScrub() on each split in the respective structure: transaction, account, account & it's children, account-group.
Definition at line 1800 of file Transaction.c.
01801 { 01802 gnc_commodity *currency; 01803 01804 if (!trans) return; 01805 01806 xaccTransBeginEdit(trans); 01807 /* The split scrub expects the transaction to have a currency! */ 01808 currency = xaccTransGetCurrency (trans); 01809 if (!currency) 01810 PERR ("Transaction doesn't have a currency!"); 01811 01812 FOR_EACH_SPLIT(trans, xaccSplitScrub(s)); 01813 xaccTransCommitEdit(trans); 01814 }
1.5.2