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. | |
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.
| void xaccAccountAssignLots | ( | Account * | acc | ) |
The xaccAccountAssignLots() routine will walk over all of the splits in an account, and make sure that each belongs to a lot. Currently, the default (and only implemented) assignment policy is a FIFO policy: Any splits that are not in a lot will be used to close the oldest open lot(s). If there are no open lots, a new lot will be started. By trying to close the oldest lots, this effectively implements a FIFO acounting policy.
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 58 of file Scrub2.c.
00059 { 00060 SplitList *splits, *node; 00061 00062 if (!acc) return; 00063 00064 ENTER ("acc=%s", xaccAccountGetName(acc)); 00065 xaccAccountBeginEdit (acc); 00066 00067 restart_loop: 00068 splits = xaccAccountGetSplitList(acc); 00069 for (node = splits; node; node = node->next) 00070 { 00071 Split * split = node->data; 00072 00073 /* If already in lot, then no-op */ 00074 if (split->lot) continue; 00075 00076 /* Skip voided transactions */ 00077 if (gnc_numeric_zero_p (split->amount) && 00078 xaccTransGetVoidStatus(split->parent)) continue; 00079 00080 if (xaccSplitAssign (split)) goto restart_loop; 00081 } 00082 xaccAccountCommitEdit (acc); 00083 LEAVE ("acc=%s", xaccAccountGetName(acc)); 00084 }
| void xaccAccountScrubCommodity | ( | Account * | account | ) |
The xaccAccountScrubCommodity method fixed accounts without a commodity by using the old account currency and security.
Definition at line 1063 of file Scrub.c.
01064 { 01065 gnc_commodity *commodity; 01066 01067 if (!account) return; 01068 if (xaccAccountGetType(account) == ACCT_TYPE_ROOT) return; 01069 01070 commodity = xaccAccountGetCommodity (account); 01071 if (commodity) return; 01072 01073 /* Use the 'obsolete' routines to try to figure out what the 01074 * account commodity should have been. */ 01075 commodity = DxaccAccountGetSecurity (account); 01076 if (commodity) 01077 { 01078 xaccAccountSetCommodity (account, commodity); 01079 return; 01080 } 01081 01082 commodity = DxaccAccountGetCurrency (account); 01083 if (commodity) 01084 { 01085 xaccAccountSetCommodity (account, commodity); 01086 return; 01087 } 01088 01089 PERR ("Account \"%s\" does not have a commodity!", 01090 xaccAccountGetName(account)); 01091 }
| 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 102 of file Scrub.c.
00103 { 00104 GList *node; 00105 const char *str; 00106 00107 if (!acc) return; 00108 00109 str = xaccAccountGetName (acc); 00110 str = str ? str : "(null)"; 00111 PINFO ("Looking for orphans in account %s \n", str); 00112 00113 for (node = xaccAccountGetSplitList(acc); node; node = node->next) 00114 { 00115 Split *split = node->data; 00116 00117 TransScrubOrphansFast (xaccSplitGetParent (split), 00118 gnc_account_get_root (acc)); 00119 } 00120 }
| 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 1121 of file Scrub.c.
01122 { 01123 if (!acc) return; 01124 01125 xaccAccountTreeForEachTransaction (acc, scrub_trans_currency_helper, NULL); 01126 01127 scrub_account_commodity_helper (acc, NULL); 01128 gnc_account_foreach_descendant (acc, scrub_account_commodity_helper, NULL); 01129 }
| 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.
| root | 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 1178 of file Scrub.c.
01179 { 01180 gboolean new_style = FALSE; 01181 ENTER(" "); 01182 01183 if (!root || !table) 01184 { 01185 LEAVE("Oops"); 01186 return; 01187 } 01188 01189 gnc_commodity_table_foreach_commodity (table, check_quote_source, &new_style); 01190 01191 move_quote_source(root, GINT_TO_POINTER(new_style)); 01192 gnc_account_foreach_descendant (root, move_quote_source, 01193 GINT_TO_POINTER(new_style)); 01194 LEAVE("Migration done"); 01195 }
| 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 96 of file Scrub2.c.
00097 { 00098 Account *acc; 00099 Split *split; 00100 GNCPolicy *pcy; 00101 00102 if (!lot) return; 00103 acc = gnc_lot_get_account(lot); 00104 pcy = gnc_account_get_policy(acc); 00105 00106 ENTER ("(lot=%s, acc=%s)", gnc_lot_get_title(lot), xaccAccountGetName(acc)); 00107 00108 /* If balance already zero, we have nothing to do. */ 00109 if (gnc_lot_is_closed (lot)) return; 00110 00111 split = pcy->PolicyGetSplit (pcy, lot); 00112 if (!split) return; /* Handle the common case */ 00113 00114 /* Reject voided transactions */ 00115 if (gnc_numeric_zero_p(split->amount) && 00116 xaccTransGetVoidStatus(split->parent)) return; 00117 00118 xaccAccountBeginEdit (acc); 00119 00120 /* Loop until we've filled up the lot, (i.e. till the 00121 * balance goes to zero) or there are no splits left. */ 00122 while (1) 00123 { 00124 Split *subsplit; 00125 00126 subsplit = xaccSplitAssignToLot (split, lot); 00127 if (subsplit == split) 00128 { 00129 PERR ("Accounting Policy gave us a split that " 00130 "doesn't fit into this lot\n" 00131 "lot baln=%s, isclosed=%d, aplit amt=%s", 00132 gnc_num_dbg_to_string (gnc_lot_get_balance(lot)), 00133 gnc_lot_is_closed (lot), 00134 gnc_num_dbg_to_string (split->amount)); 00135 break; 00136 } 00137 00138 if (gnc_lot_is_closed (lot)) break; 00139 00140 split = pcy->PolicyGetSplit (pcy, lot); 00141 if (!split) break; 00142 } 00143 xaccAccountCommitEdit (acc); 00144 LEAVE ("(lot=%s, acc=%s)", gnc_lot_get_title(lot), xaccAccountGetName(acc)); 00145 }
| 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 150 of file Scrub2.c.
00151 { 00152 gnc_commodity *currency = NULL; 00153 SplitList *snode; 00154 GList *node; 00155 gnc_numeric zero = gnc_numeric_zero(); 00156 gnc_numeric value = zero; 00157 00158 if (!lot) return; 00159 00160 ENTER ("lot=%s", kvp_frame_get_string (gnc_lot_get_slots (lot), "/title")); 00161 00162 for (snode = gnc_lot_get_split_list(lot); snode; snode = snode->next) 00163 { 00164 Split *s = snode->data; 00165 xaccSplitComputeCapGains (s, NULL); 00166 } 00167 00168 /* We double-check only closed lots */ 00169 if (FALSE == gnc_lot_is_closed (lot)) return; 00170 00171 for (snode = gnc_lot_get_split_list(lot); snode; snode = snode->next) 00172 { 00173 Split *s = snode->data; 00174 Transaction *trans = s->parent; 00175 00176 /* Check to make sure all splits in the lot have a common currency */ 00177 if (NULL == currency) 00178 { 00179 currency = trans->common_currency; 00180 } 00181 if (FALSE == gnc_commodity_equiv (currency, trans->common_currency)) 00182 { 00183 /* This lot has mixed currencies. Can't double-balance. 00184 * Silently punt */ 00185 PWARN ("Lot with multiple currencies:\n" 00186 "\ttrans=%s curr=%s", xaccTransGetDescription(trans), 00187 gnc_commodity_get_fullname(trans->common_currency)); 00188 break; 00189 } 00190 00191 /* Now, total up the values */ 00192 value = gnc_numeric_add (value, xaccSplitGetValue (s), 00193 GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT); 00194 PINFO ("Split=%p value=%s Accum Lot value=%s", s, 00195 gnc_num_dbg_to_string (s->value), 00196 gnc_num_dbg_to_string (value)); 00197 00198 } 00199 00200 if (FALSE == gnc_numeric_equal (value, zero)) 00201 { 00202 /* Unhandled error condition. Not sure what to do here, 00203 * Since the ComputeCapGains should have gotten it right. 00204 * I suppose there might be small rounding errors, a penny or two, 00205 * the ideal thing would to figure out why there's a rounding 00206 * error, and fix that. 00207 */ 00208 PERR ("Closed lot fails to double-balance !! lot value=%s", 00209 gnc_num_dbg_to_string (value)); 00210 for (node = gnc_lot_get_split_list(lot); node; node = node->next) 00211 { 00212 Split *s = node->data; 00213 PERR ("s=%p amt=%s val=%s", s, 00214 gnc_num_dbg_to_string(s->amount), 00215 gnc_num_dbg_to_string(s->value)); 00216 } 00217 } 00218 00219 LEAVE ("lot=%s", kvp_frame_get_string (gnc_lot_get_slots (lot), "/title")); 00220 }
| 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 396 of file Scrub2.c.
00397 { 00398 gboolean rc = FALSE; 00399 Transaction *txn; 00400 SplitList *node; 00401 GNCLot *lot; 00402 const GUID *guid; 00403 00404 if (FALSE == is_subsplit (split)) return FALSE; 00405 00406 txn = split->parent; 00407 lot = xaccSplitGetLot (split); 00408 00409 ENTER ("(Lot=%s)", gnc_lot_get_title(lot)); 00410 restart: 00411 for (node = txn->splits; node; node = node->next) 00412 { 00413 Split *s = node->data; 00414 if (xaccSplitGetLot (s) != lot) continue; 00415 if (s == split) continue; 00416 if (qof_instance_get_destroying(s)) continue; 00417 00418 /* OK, this split is in the same lot (and thus same account) 00419 * as the indicated split. Make sure it is really a subsplit 00420 * of the split we started with. It's possible to have two 00421 * splits in the same lot and transaction that are not subsplits 00422 * of each other, the test-period test suite does this, for 00423 * example. Only worry about adjacent sub-splits. By 00424 * repeatedly merging adjacent subsplits, we'll get the non- 00425 * adjacent ones too. */ 00426 guid = qof_instance_get_guid(s); 00427 if (gnc_kvp_bag_find_by_guid (split->inst.kvp_data, "lot-split", 00428 "peer_guid", guid) == NULL) 00429 continue; 00430 00431 merge_splits (split, s); 00432 rc = TRUE; 00433 goto restart; 00434 } 00435 if (gnc_numeric_zero_p (split->amount)) 00436 { 00437 PWARN ("Result of merge has zero amt!"); 00438 } 00439 LEAVE (" splits merged=%d", rc); 00440 return rc; 00441 }
| 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 243 of file Scrub2.c.
00244 { 00245 gnc_numeric src_amt, src_val; 00246 SplitList *node; 00247 00248 if (FALSE == is_subsplit (split)) return; 00249 00250 ENTER (" "); 00251 /* Get 'price' of the indicated split */ 00252 src_amt = xaccSplitGetAmount (split); 00253 src_val = xaccSplitGetValue (split); 00254 00255 /* Loop over splits, adjust each so that it has the same 00256 * ratio (i.e. price). Change the value to get things 00257 * right; do not change the amount */ 00258 for (node = split->parent->splits; node; node = node->next) 00259 { 00260 Split *s = node->data; 00261 Transaction *txn = s->parent; 00262 gnc_numeric dst_amt, dst_val, target_val; 00263 gnc_numeric frac, delta; 00264 int scu; 00265 00266 /* Skip the reference split */ 00267 if (s == split) continue; 00268 00269 scu = gnc_commodity_get_fraction (txn->common_currency); 00270 00271 dst_amt = xaccSplitGetAmount (s); 00272 dst_val = xaccSplitGetValue (s); 00273 frac = gnc_numeric_div (dst_amt, src_amt, 00274 GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE); 00275 target_val = gnc_numeric_mul (frac, src_val, 00276 scu, GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND); 00277 if (gnc_numeric_check (target_val)) 00278 { 00279 PERR ("Numeric overflow of value\n" 00280 "\tAcct=%s txn=%s\n" 00281 "\tdst_amt=%s src_val=%s src_amt=%s\n", 00282 xaccAccountGetName (s->acc), 00283 xaccTransGetDescription(txn), 00284 gnc_num_dbg_to_string(dst_amt), 00285 gnc_num_dbg_to_string(src_val), 00286 gnc_num_dbg_to_string(src_amt)); 00287 continue; 00288 } 00289 00290 /* If the required price changes are 'small', do nothing. 00291 * That is a case that the user will have to deal with 00292 * manually. This routine is really intended only for 00293 * a gross level of synchronization. 00294 */ 00295 delta = gnc_numeric_sub_fixed (target_val, dst_val); 00296 delta = gnc_numeric_abs (delta); 00297 if (maxmult * delta.num < delta.denom) continue; 00298 00299 /* If the amount is small, pass on that too */ 00300 if ((-maxamtscu < dst_amt.num) && (dst_amt.num < maxamtscu)) continue; 00301 00302 /* Make the actual adjustment */ 00303 xaccTransBeginEdit (txn); 00304 xaccSplitSetValue (s, target_val); 00305 xaccTransCommitEdit (txn); 00306 } 00307 LEAVE (" "); 00308 }
| 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 commodity 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 173 of file Scrub.c.
00174 { 00175 Account *account; 00176 Transaction *trans; 00177 gnc_numeric value, amount; 00178 gnc_commodity *currency, *acc_commodity; 00179 int scu; 00180 00181 if (!split) return; 00182 ENTER ("(split=%p)", split); 00183 00184 trans = xaccSplitGetParent (split); 00185 if (!trans) 00186 { 00187 LEAVE("no trans"); 00188 return; 00189 } 00190 00191 account = xaccSplitGetAccount (split); 00192 00193 /* If there's no account, this split is an orphan. 00194 * We need to fix that first, before proceeding. 00195 */ 00196 if (!account) 00197 { 00198 xaccTransScrubOrphans (trans); 00199 account = xaccSplitGetAccount (split); 00200 } 00201 00202 /* Grrr... the register gnc_split_register_load() line 203 of 00203 * src/register/ledger-core/split-register-load.c will create 00204 * free-floating bogus transactions. Ignore these for now ... 00205 */ 00206 if (!account) 00207 { 00208 PINFO ("Free Floating Transaction!"); 00209 LEAVE ("no account"); 00210 return; 00211 } 00212 00213 /* Split amounts and values should be valid numbers */ 00214 value = xaccSplitGetValue (split); 00215 if (gnc_numeric_check (value)) 00216 { 00217 value = gnc_numeric_zero(); 00218 xaccSplitSetValue (split, value); 00219 } 00220 00221 amount = xaccSplitGetAmount (split); 00222 if (gnc_numeric_check (amount)) 00223 { 00224 amount = gnc_numeric_zero(); 00225 xaccSplitSetAmount (split, amount); 00226 } 00227 00228 currency = xaccTransGetCurrency (trans); 00229 00230 /* If the account doesn't have a commodity, 00231 * we should attempt to fix that first. 00232 */ 00233 acc_commodity = xaccAccountGetCommodity(account); 00234 if (!acc_commodity) 00235 { 00236 xaccAccountScrubCommodity (account); 00237 } 00238 if (!acc_commodity || !gnc_commodity_equiv(acc_commodity, currency)) 00239 { 00240 LEAVE ("(split=%p) inequiv currency", split); 00241 return; 00242 } 00243 00244 scu = MIN (xaccAccountGetCommoditySCU (account), 00245 gnc_commodity_get_fraction (currency)); 00246 00247 if (gnc_numeric_same (amount, value, scu, GNC_HOW_RND_ROUND)) 00248 { 00249 LEAVE("(split=%p) different values", split); 00250 return; 00251 } 00252 00253 /* 00254 * This will be hit every time you answer yes to the dialog "The 00255 * current transaction has changed. Would you like to record it. 00256 */ 00257 PINFO ("Adjusted split with mismatched values, desc=\"%s\" memo=\"%s\"" 00258 " old amount %s %s, new amount %s", 00259 trans->description, split->memo, 00260 gnc_num_dbg_to_string (xaccSplitGetAmount(split)), 00261 gnc_commodity_get_mnemonic (currency), 00262 gnc_num_dbg_to_string (xaccSplitGetValue(split))); 00263 00264 xaccTransBeginEdit (trans); 00265 xaccSplitSetAmount (split, value); 00266 xaccTransCommitEdit (trans); 00267 LEAVE ("(split=%p)", split); 00268 }
| 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 954 of file Scrub.c.
00955 { 00956 SplitList *node; 00957 gnc_commodity *currency; 00958 00959 if (!trans) return; 00960 00961 /* If there are any orphaned splits in a transaction, then the 00962 * this routine will fail. Therefore, we want to make sure that 00963 * there are no orphans (splits without parent account). 00964 */ 00965 xaccTransScrubOrphans (trans); 00966 00967 currency = xaccTransGetCurrency (trans); 00968 if (currency) return; 00969 00970 currency = xaccTransFindOldCommonCurrency (trans, qof_instance_get_book(trans)); 00971 if (currency) 00972 { 00973 xaccTransBeginEdit (trans); 00974 xaccTransSetCurrency (trans, currency); 00975 xaccTransCommitEdit (trans); 00976 } 00977 else 00978 { 00979 if (NULL == trans->splits) 00980 { 00981 PWARN ("Transaction \"%s\" has no splits in it!", trans->description); 00982 } 00983 else 00984 { 00985 SplitList *node; 00986 char guid_str[GUID_ENCODING_LENGTH+1]; 00987 guid_to_string_buff(xaccTransGetGUID(trans), guid_str); 00988 PWARN ("no common transaction currency found for trans=\"%s\" (%s)", 00989 trans->description, guid_str); 00990 00991 for (node = trans->splits; node; node = node->next) 00992 { 00993 Split *split = node->data; 00994 if (NULL == split->acc) 00995 { 00996 PWARN (" split=\"%s\" is not in any account!", split->memo); 00997 } 00998 else 00999 { 01000 PWARN (" split=\"%s\" account=\"%s\" commodity=\"%s\"", 01001 split->memo, xaccAccountGetName(split->acc), 01002 gnc_commodity_get_mnemonic(xaccAccountGetCommodity(split->acc))); 01003 } 01004 } 01005 } 01006 } 01007 01008 for (node = trans->splits; node; node = node->next) 01009 { 01010 Split *sp = node->data; 01011 01012 if (!gnc_numeric_equal(xaccSplitGetAmount (sp), 01013 xaccSplitGetValue (sp))) 01014 { 01015 gnc_commodity *acc_currency; 01016 01017 acc_currency = sp->acc ? xaccAccountGetCommodity(sp->acc) : NULL; 01018 if (acc_currency == currency) 01019 { 01020 /* This Split needs fixing: The transaction-currency equals 01021 * the account-currency/commodity, but the amount/values are 01022 * inequal i.e. they still correspond to the security 01023 * (amount) and the currency (value). In the new model, the 01024 * value is the amount in the account-commodity -- so it 01025 * needs to be set to equal the amount (since the 01026 * account-currency doesn't exist anymore). 01027 * 01028 * Note: Nevertheless we lose some information here. Namely, 01029 * the information that the 'amount' in 'account-old-security' 01030 * was worth 'value' in 'account-old-currency'. Maybe it would 01031 * be better to store that information in the price database? 01032 * But then, for old currency transactions there is still the 01033 * 'other' transaction, which is going to keep that 01034 * information. So I don't bother with that here. -- cstim, 01035 * 2002/11/20. */ 01036 01037 PWARN ("Adjusted split with mismatched values, desc=\"%s\" memo=\"%s\"" 01038 " old amount %s %s, new amount %s", 01039 trans->description, sp->memo, 01040 gnc_num_dbg_to_string (xaccSplitGetAmount(sp)), 01041 gnc_commodity_get_mnemonic (currency), 01042 gnc_num_dbg_to_string (xaccSplitGetValue(sp))); 01043 xaccTransBeginEdit (trans); 01044 xaccSplitSetAmount (sp, xaccSplitGetValue(sp)); 01045 xaccTransCommitEdit (trans); 01046 } 01047 /*else 01048 { 01049 PINFO ("Ok: Split '%s' Amount %s %s, value %s %s", 01050 xaccSplitGetMemo (sp), 01051 gnc_num_dbg_to_string (amount), 01052 gnc_commodity_get_mnemonic (currency), 01053 gnc_num_dbg_to_string (value), 01054 gnc_commodity_get_mnemonic (acc_currency)); 01055 }*/ 01056 } 01057 } 01058 }
| 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 304 of file Scrub.c.
00305 { 00306 GList *node; 00307 gnc_commodity *common_currency = NULL; 00308 00309 if (!trans) return; 00310 00311 for (node = xaccTransGetSplitList (trans); node; node = node->next) 00312 { 00313 Split *split = node->data; 00314 00315 if (!xaccTransStillHasSplit(trans, split)) continue; 00316 if (gnc_numeric_equal(xaccSplitGetAmount (split), 00317 xaccSplitGetValue (split))) 00318 { 00319 00320 Account *s_account = xaccSplitGetAccount (split); 00321 gnc_commodity *s_commodity = xaccAccountGetCommodity (s_account); 00322 00323 if (s_commodity) 00324 { 00325 if (gnc_commodity_is_currency(s_commodity)) 00326 { 00327 /* Found a split where the amount is the same as the value and 00328 the commodity is a currency. If all splits in the transaction 00329 that fit this description are in the same currency then the 00330 transaction should be in that currency too. */ 00331 00332 if (common_currency == NULL) 00333 /* First one we've found, save the currency */ 00334 common_currency = s_commodity; 00335 else if ( !gnc_commodity_equiv (common_currency, s_commodity)) 00336 { 00337 /* Splits are inconsistent, more than one has a value equal to 00338 the amount, but they aren't all in the same currency. */ 00339 common_currency = NULL; 00340 break; 00341 } 00342 } 00343 } 00344 } 00345 } 00346 00347 if (common_currency && 00348 !gnc_commodity_equiv (common_currency, xaccTransGetCurrency (trans))) 00349 { 00350 00351 /* Found a common currency for the splits, and the transaction is not 00352 in that currency */ 00353 gboolean trans_was_open; 00354 00355 PINFO ("transaction in wrong currency"); 00356 00357 trans_was_open = xaccTransIsOpen (trans); 00358 00359 if (!trans_was_open) 00360 xaccTransBeginEdit (trans); 00361 00362 xaccTransSetCurrency (trans, common_currency); 00363 00364 if (!trans_was_open) 00365 xaccTransCommitEdit (trans); 00366 } 00367 }
| 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 584 of file Scrub.c.
00586 { 00587 const gnc_commodity *currency; 00588 00589 if (!trans) return; 00590 00591 ENTER ("()"); 00592 00593 /* Must look for orphan splits even if there is no imbalance. */ 00594 xaccTransScrubSplits (trans); 00595 00596 /* Return immediately if things are balanced. */ 00597 if (xaccTransIsBalanced (trans)) 00598 return; 00599 00600 currency = xaccTransGetCurrency (trans); 00601 00602 if (! xaccTransUseTradingAccounts (trans)) 00603 { 00604 gnc_numeric imbalance; 00605 00606 /* Make the value sum to zero */ 00607 imbalance = xaccTransGetImbalanceValue (trans); 00608 if (! gnc_numeric_zero_p (imbalance)) 00609 { 00610 PINFO ("Value unbalanced transaction"); 00611 00612 add_balance_split (trans, imbalance, root, account); 00613 } 00614 } 00615 else 00616 { 00617 MonetaryList *imbal_list; 00618 MonetaryList *imbalance_commod; 00619 GList *splits; 00620 gnc_numeric imbalance; 00621 Split *balance_split = NULL; 00622 00623 /* If there are existing trading splits, adjust the price or exchange 00624 rate in each of them to agree with the non-trading splits for the 00625 same commodity. If there are multiple non-trading splits for the 00626 same commodity in the transaction this will use the exchange rate in 00627 the last such split. This shouldn't happen, and if it does then there's 00628 not much we can do about it anyway. 00629 00630 While we're at it, compute the value imbalance ignoring existing 00631 trading splits. */ 00632 00633 imbalance = gnc_numeric_zero(); 00634 00635 for (splits = trans->splits; splits; splits = splits->next) 00636 { 00637 Split *split = splits->data; 00638 gnc_numeric value, amount; 00639 gnc_commodity *commodity; 00640 00641 if (! xaccTransStillHasSplit (trans, split)) continue; 00642 00643 commodity = xaccAccountGetCommodity (xaccSplitGetAccount(split)); 00644 if (!commodity) 00645 { 00646 PERR("Split has no commodity"); 00647 continue; 00648 } 00649 00650 balance_split = find_trading_split (trans, root, commodity); 00651 00652 if (balance_split != split) 00653 /* this is not a trading split */ 00654 imbalance = gnc_numeric_add(imbalance, xaccSplitGetValue (split), 00655 GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT); 00656 00657 /* Ignore splits where value or amount is zero */ 00658 value = xaccSplitGetValue (split); 00659 amount = xaccSplitGetAmount (split); 00660 if (gnc_numeric_zero_p(amount) || gnc_numeric_zero_p(value)) 00661 continue; 00662 00663 if (balance_split && balance_split != split) 00664 { 00665 gnc_numeric convrate = gnc_numeric_div (amount, value, 00666 GNC_DENOM_AUTO, GNC_DENOM_REDUCE); 00667 gnc_numeric old_value, new_value; 00668 old_value = xaccSplitGetValue(balance_split); 00669 new_value = gnc_numeric_div (xaccSplitGetAmount(balance_split), 00670 convrate, 00671 gnc_commodity_get_fraction(currency), 00672 GNC_RND_ROUND); 00673 if (! gnc_numeric_equal (old_value, new_value)) 00674 { 00675 xaccTransBeginEdit (trans); 00676 xaccSplitSetValue (balance_split, new_value); 00677 xaccSplitScrub (balance_split); 00678 xaccTransCommitEdit (trans); 00679 } 00680 } 00681 } 00682 00683 /* Balance the value, ignoring existing trading splits */ 00684 if (! gnc_numeric_zero_p (imbalance)) 00685 { 00686 PINFO ("Value unbalanced transaction"); 00687 00688 add_balance_split (trans, imbalance, root, account); 00689 } 00690 00691 /* If the transaction is balanced, nothing more to do */ 00692 imbal_list = xaccTransGetImbalance (trans); 00693 if (!imbal_list) 00694 { 00695 LEAVE("()"); 00696 return; 00697 } 00698 00699 PINFO ("Currency unbalanced transaction"); 00700 00701 for (imbalance_commod = imbal_list; imbalance_commod; 00702 imbalance_commod = imbalance_commod->next) 00703 { 00704 gnc_monetary *imbal_mon = imbalance_commod->data; 00705 gnc_commodity *commodity; 00706 gnc_numeric old_amount, new_amount; 00707 gnc_numeric old_value, new_value, val_imbalance; 00708 GList *splits; 00709 00710 commodity = gnc_monetary_commodity (*imbal_mon); 00711 00712 balance_split = get_trading_split(trans, root, commodity); 00713 if (!balance_split) 00714 { 00715 /* Error already logged */ 00716 gnc_monetary_list_free(imbal_list); 00717 LEAVE(""); 00718 return; 00719 } 00720 00721 account = xaccSplitGetAccount(balance_split); 00722 00723 if (! gnc_commodity_equal (currency, commodity)) 00724 { 00725 /* Find the value imbalance in this commodity */ 00726 val_imbalance = gnc_numeric_zero(); 00727 for (splits = trans->splits; splits; splits = splits->next) 00728 { 00729 Split *split = splits->data; 00730 if (xaccTransStillHasSplit (trans, split) && 00731 gnc_commodity_equal (commodity, 00732 xaccAccountGetCommodity(xaccSplitGetAccount(split)))) 00733 val_imbalance = gnc_numeric_add (val_imbalance, xaccSplitGetValue (split), 00734 GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT); 00735 } 00736 } 00737 00738 xaccTransBeginEdit (trans); 00739 00740 old_amount = xaccSplitGetAmount (balance_split); 00741 new_amount = gnc_numeric_sub (old_amount, gnc_monetary_value(*imbal_mon), 00742 gnc_commodity_get_fraction(commodity), 00743 GNC_HOW_RND_ROUND); 00744 00745 xaccSplitSetAmount (balance_split, new_amount); 00746 00747 if (gnc_commodity_equal (currency, commodity)) 00748 { 00749 /* Imbalance commodity is the transaction currency, value in the 00750 split must be the same as the amount */ 00751 xaccSplitSetValue (balance_split, new_amount); 00752 } 00753 else 00754 { 00755 old_value = xaccSplitGetValue (balance_split); 00756 new_value = gnc_numeric_sub (old_value, val_imbalance, 00757 gnc_commodity_get_fraction(currency), 00758 GNC_HOW_RND_ROUND); 00759 00760 xaccSplitSetValue (balance_split, new_value); 00761 } 00762 00763 xaccSplitScrub (balance_split); 00764 xaccTransCommitEdit (trans); 00765 } 00766 00767 gnc_monetary_list_free(imbal_list); 00768 00769 if (!gnc_numeric_zero_p(xaccTransGetImbalanceValue(trans))) 00770 { 00771 /* This is probably because there are splits with zero amount 00772 and non-zero value. These are usually realized gain/loss 00773 splits. Add a reversing split for each of them to balance 00774 the value. */ 00775 00776 /* Copy the split list so we don't see the splits we're adding */ 00777 GList *splits_dup = g_list_copy(trans->splits); 00778 for (splits = splits_dup; splits; splits = splits->next) 00779 { 00780 Split *split = splits->data; 00781 if (! xaccTransStillHasSplit(trans, split)) continue; 00782 if (!gnc_numeric_zero_p(xaccSplitGetValue(split)) && 00783 gnc_numeric_zero_p(xaccSplitGetAmount(split))) 00784 { 00785 gnc_commodity *commodity; 00786 gnc_numeric old_value, new_value; 00787 00788 commodity = xaccAccountGetCommodity(xaccSplitGetAccount(split)); 00789 if (!commodity) 00790 { 00791 PERR("Split has no commodity"); 00792 continue; 00793 } 00794 balance_split = get_trading_split(trans, root, commodity); 00795 if (!balance_split) 00796 { 00797 /* Error already logged */ 00798 gnc_monetary_list_free(imbal_list); 00799 LEAVE(""); 00800 return; 00801 } 00802 account = xaccSplitGetAccount(balance_split); 00803 00804 xaccTransBeginEdit (trans); 00805 00806 old_value = xaccSplitGetValue (balance_split); 00807 new_value = gnc_numeric_sub (old_value, xaccSplitGetValue(split), 00808 gnc_commodity_get_fraction(currency), 00809 GNC_HOW_RND_ROUND); 00810 xaccSplitSetValue (balance_split, new_value); 00811 00812 /* Don't change the balance split's amount since the amount 00813 is zero in the split we're working on */ 00814 00815 xaccSplitScrub (balance_split); 00816 xaccTransCommitEdit (trans); 00817 } 00818 } 00819 00820 g_list_free(splits_dup); 00821 00822 if (!gnc_numeric_zero_p(xaccTransGetImbalanceValue(trans))) 00823 PERR("Balancing currencies unbalanced value"); 00824 } 00825 } 00826 LEAVE ("()"); 00827 }
| void xaccTransScrubOrphans | ( | Transaction * | trans | ) |
The xaccTransScrubOrphans() method scrubs only the splits in the given transaction.
Definition at line 124 of file Scrub.c.
00125 { 00126 SplitList *node; 00127 QofBook *book = NULL; 00128 Account *root = NULL; 00129 for (node = trans->splits; node; node = node->next) 00130 { 00131 Split *split = node->data; 00132 00133 if (split->acc) 00134 { 00135 TransScrubOrphansFast (trans, gnc_account_get_root(split->acc)); 00136 return; 00137 } 00138 } 00139 00140 /* If we got to here, then *none* of the splits belonged to an 00141 * account. Not a happy situation. We should dig an account 00142 * out of the book the transaction belongs to. 00143 * XXX we should probably *always* to this, instead of the above loop! 00144 */ 00145 PINFO ("Free Floating Transaction!"); 00146 book = xaccTransGetBook (trans); 00147 root = gnc_book_get_root_account (book); 00148 TransScrubOrphansFast (trans, root); 00149 }
| 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 2145 of file Transaction.c.
02146 { 02147 gnc_commodity *currency; 02148 02149 if (!trans) return; 02150 02151 xaccTransBeginEdit(trans); 02152 /* The split scrub expects the transaction to have a currency! */ 02153 currency = xaccTransGetCurrency (trans); 02154 if (!currency) 02155 PERR ("Transaction doesn't have a currency!"); 02156 02157 FOR_EACH_SPLIT(trans, xaccSplitScrub(s)); 02158 xaccTransCommitEdit(trans); 02159 }
1.5.7.1