|
GnuCash 2.3.0
|
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) |
| gboolean | xaccScrubMergeSubSplits (Split *split) |
| 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) |
Data scrubbing, repairing and forward migration routines. These routines check and repair data, making sure that it is in a format that the current version of the GnuCash Engine likes. These routines serve both to provide backwards compatibility with older versions of GnuCash, and to fix or at least paper over possible current problems.
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.
{
SplitList *splits, *node;
if (!acc) return;
ENTER ("acc=%s", xaccAccountGetName(acc));
xaccAccountBeginEdit (acc);
restart_loop:
splits = xaccAccountGetSplitList(acc);
for (node = splits; node; node = node->next)
{
Split * split = node->data;
/* If already in lot, then no-op */
if (split->lot) continue;
/* Skip voided transactions */
if (gnc_numeric_zero_p (split->amount) &&
xaccTransGetVoidStatus(split->parent)) continue;
if (xaccSplitAssign (split)) goto restart_loop;
}
xaccAccountCommitEdit (acc);
LEAVE ("acc=%s", xaccAccountGetName(acc));
}
| void xaccAccountScrubCommodity | ( | Account * | account | ) |
The xaccAccountScrubCommodity method fixed accounts without a commodity by using the old account currency and security.
Definition at line 1178 of file Scrub.c.
{
gnc_commodity *commodity;
if (!account) return;
if (xaccAccountGetType(account) == ACCT_TYPE_ROOT) return;
commodity = xaccAccountGetCommodity (account);
if (commodity) return;
/* Use the 'obsolete' routines to try to figure out what the
* account commodity should have been. */
commodity = xaccAccountGetCommodity (account);
if (commodity)
{
xaccAccountSetCommodity (account, commodity);
return;
}
commodity = DxaccAccountGetCurrency (account);
if (commodity)
{
xaccAccountSetCommodity (account, commodity);
return;
}
PERR ("Account \"%s\" does not have a commodity!",
xaccAccountGetName(account));
}
| 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.
{
LotList *lots, *node;
if (!acc) return;
if (FALSE == xaccAccountHasTrades (acc)) return;
ENTER ("(acc=%s)", xaccAccountGetName(acc));
xaccAccountBeginEdit(acc);
xaccAccountAssignLots (acc);
lots = xaccAccountGetLotList(acc);
for (node = lots; node; node = node->next)
{
GNCLot *lot = node->data;
xaccScrubLot (lot);
}
g_list_free(lots);
xaccAccountCommitEdit(acc);
LEAVE ("(acc=%s)", xaccAccountGetName(acc));
}
| 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.
{
GList *node;
const char *str;
if (!acc) return;
str = xaccAccountGetName (acc);
str = str ? str : "(null)";
PINFO ("Looking for orphans in account %s \n", str);
for (node = xaccAccountGetSplitList(acc); node; node = node->next)
{
Split *split = node->data;
TransScrubOrphansFast (xaccSplitGetParent (split),
gnc_account_get_root (acc));
}
}
| 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 1239 of file Scrub.c.
{
if (!acc) return;
xaccAccountTreeForEachTransaction (acc, scrub_trans_currency_helper, NULL);
scrub_account_commodity_helper (acc, NULL);
gnc_account_foreach_descendant (acc, scrub_account_commodity_helper, NULL);
}
| 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.
{
if (!acc) return;
xaccAccountScrubOrphans (acc);
gnc_account_foreach_descendant(acc,
(AccountCb)xaccAccountScrubOrphans, NULL);
}
| 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 1296 of file Scrub.c.
{
gboolean new_style = FALSE;
ENTER(" ");
if (!root || !table)
{
LEAVE("Oops");
return;
}
gnc_commodity_table_foreach_commodity (table, check_quote_source, &new_style);
move_quote_source(root, GINT_TO_POINTER(new_style));
gnc_account_foreach_descendant (root, move_quote_source,
GINT_TO_POINTER(new_style));
LEAVE("Migration done");
}
| 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.
{
Account *acc;
Split *split;
GNCPolicy *pcy;
if (!lot) return;
acc = gnc_lot_get_account(lot);
pcy = gnc_account_get_policy(acc);
ENTER ("(lot=%s, acc=%s)", gnc_lot_get_title(lot), xaccAccountGetName(acc));
/* If balance already zero, we have nothing to do. */
if (gnc_lot_is_closed (lot)) return;
split = pcy->PolicyGetSplit (pcy, lot);
if (!split) return; /* Handle the common case */
/* Reject voided transactions */
if (gnc_numeric_zero_p(split->amount) &&
xaccTransGetVoidStatus(split->parent)) return;
xaccAccountBeginEdit (acc);
/* Loop until we've filled up the lot, (i.e. till the
* balance goes to zero) or there are no splits left. */
while (1)
{
Split *subsplit;
subsplit = xaccSplitAssignToLot (split, lot);
if (subsplit == split)
{
PERR ("Accounting Policy gave us a split that "
"doesn't fit into this lot\n"
"lot baln=%s, isclosed=%d, aplit amt=%s",
gnc_num_dbg_to_string (gnc_lot_get_balance(lot)),
gnc_lot_is_closed (lot),
gnc_num_dbg_to_string (split->amount));
break;
}
if (gnc_lot_is_closed (lot)) break;
split = pcy->PolicyGetSplit (pcy, lot);
if (!split) break;
}
xaccAccountCommitEdit (acc);
LEAVE ("(lot=%s, acc=%s)", gnc_lot_get_title(lot), xaccAccountGetName(acc));
}
| 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.
{
gnc_commodity *currency = NULL;
SplitList *snode;
GList *node;
gnc_numeric zero = gnc_numeric_zero();
gnc_numeric value = zero;
if (!lot) return;
ENTER ("lot=%s", kvp_frame_get_string (gnc_lot_get_slots (lot), "/title"));
for (snode = gnc_lot_get_split_list(lot); snode; snode = snode->next)
{
Split *s = snode->data;
xaccSplitComputeCapGains (s, NULL);
}
/* We double-check only closed lots */
if (FALSE == gnc_lot_is_closed (lot)) return;
for (snode = gnc_lot_get_split_list(lot); snode; snode = snode->next)
{
Split *s = snode->data;
Transaction *trans = s->parent;
/* Check to make sure all splits in the lot have a common currency */
if (NULL == currency)
{
currency = trans->common_currency;
}
if (FALSE == gnc_commodity_equiv (currency, trans->common_currency))
{
/* This lot has mixed currencies. Can't double-balance.
* Silently punt */
PWARN ("Lot with multiple currencies:\n"
"\ttrans=%s curr=%s", xaccTransGetDescription(trans),
gnc_commodity_get_fullname(trans->common_currency));
break;
}
/* Now, total up the values */
value = gnc_numeric_add (value, xaccSplitGetValue (s),
GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT);
PINFO ("Split=%p value=%s Accum Lot value=%s", s,
gnc_num_dbg_to_string (s->value),
gnc_num_dbg_to_string (value));
}
if (FALSE == gnc_numeric_equal (value, zero))
{
/* Unhandled error condition. Not sure what to do here,
* Since the ComputeCapGains should have gotten it right.
* I suppose there might be small rounding errors, a penny or two,
* the ideal thing would to figure out why there's a rounding
* error, and fix that.
*/
PERR ("Closed lot fails to double-balance !! lot value=%s",
gnc_num_dbg_to_string (value));
for (node = gnc_lot_get_split_list(lot); node; node = node->next)
{
Split *s = node->data;
PERR ("s=%p amt=%s val=%s", s,
gnc_num_dbg_to_string(s->amount),
gnc_num_dbg_to_string(s->value));
}
}
LEAVE ("lot=%s", kvp_frame_get_string (gnc_lot_get_slots (lot), "/title"));
}
| 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.
{
gboolean splits_deleted = FALSE;
gnc_numeric lot_baln;
gboolean opening_baln_is_pos, lot_baln_is_pos;
Account *acc;
GNCPolicy *pcy;
if (!lot) return FALSE;
ENTER ("(lot=%p) %s", lot, gnc_lot_get_title(lot));
acc = gnc_lot_get_account (lot);
pcy = gnc_account_get_policy(acc);
xaccAccountBeginEdit(acc);
xaccScrubMergeLotSubSplits (lot);
/* If the lot balance is zero, we don't need to rebalance */
lot_baln = gnc_lot_get_balance (lot);
PINFO ("lot baln=%s for %s", gnc_num_dbg_to_string (lot_baln),
gnc_lot_get_title(lot));
if (! gnc_numeric_zero_p (lot_baln))
{
SplitList *node;
gnc_numeric opening_baln;
/* Get the opening balance for this lot */
pcy->PolicyGetLotOpening (pcy, lot, &opening_baln, NULL, NULL);
PINFO ("lot opener baln=%s", gnc_num_dbg_to_string (opening_baln));
/* If the lot is fat, give the boot to all the non-opening
* splits, and refill it */
opening_baln_is_pos = gnc_numeric_positive_p(opening_baln);
lot_baln_is_pos = gnc_numeric_positive_p(lot_baln);
if ((opening_baln_is_pos || lot_baln_is_pos) &&
((!opening_baln_is_pos) || (!lot_baln_is_pos)))
{
rethin:
for (node = gnc_lot_get_split_list(lot); node; node = node->next)
{
Split *s = node->data;
if (pcy->PolicyIsOpeningSplit (pcy, lot, s)) continue;
gnc_lot_remove_split (lot, s);
goto rethin;
}
}
/* At this point the lot is thin, so try to fill it */
xaccLotFill (lot);
/* Make sure there are no subsplits. */
splits_deleted = xaccScrubMergeLotSubSplits (lot);
}
/* Now re-compute cap gains, and then double-check that.
* But we only compute cap-gains if gains are possible;
* that is if the lot commodity is not the same as the
* currency. That is, one can't possibly have gains
* selling dollars for dollars. The business modules
* use lots with lot commodity == lot currency.
*/
if (gains_possible (lot))
{
xaccLotComputeCapGains (lot, NULL);
xaccLotScrubDoubleBalance (lot);
}
xaccAccountCommitEdit(acc);
LEAVE ("(lot=%s, deleted=%d)", gnc_lot_get_title(lot), splits_deleted);
return splits_deleted;
}
| 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 329 of file Scrub2.c.
{
gboolean rc = FALSE;
Transaction *txn;
SplitList *node;
GNCLot *lot;
const GncGUID *guid;
if (FALSE == is_subsplit (split)) return FALSE;
txn = split->parent;
lot = xaccSplitGetLot (split);
ENTER ("(Lot=%s)", gnc_lot_get_title(lot));
restart:
for (node = txn->splits; node; node = node->next)
{
Split *s = node->data;
if (xaccSplitGetLot (s) != lot) continue;
if (s == split) continue;
if (qof_instance_get_destroying(s)) continue;
/* OK, this split is in the same lot (and thus same account)
* as the indicated split. Make sure it is really a subsplit
* of the split we started with. It's possible to have two
* splits in the same lot and transaction that are not subsplits
* of each other, the test-period test suite does this, for
* example. Only worry about adjacent sub-splits. By
* repeatedly merging adjacent subsplits, we'll get the non-
* adjacent ones too. */
guid = qof_instance_get_guid(s);
if (gnc_kvp_bag_find_by_guid (split->inst.kvp_data, "lot-split",
"peer_guid", guid) == NULL)
continue;
merge_splits (split, s);
rc = TRUE;
goto restart;
}
if (gnc_numeric_zero_p (split->amount))
{
PWARN ("Result of merge has zero amt!");
}
LEAVE (" splits merged=%d", rc);
return rc;
}
| 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.
{
Account *account;
Transaction *trans;
gnc_numeric value, amount;
gnc_commodity *currency, *acc_commodity;
int scu;
if (!split) return;
ENTER ("(split=%p)", split);
trans = xaccSplitGetParent (split);
if (!trans)
{
LEAVE("no trans");
return;
}
account = xaccSplitGetAccount (split);
/* If there's no account, this split is an orphan.
* We need to fix that first, before proceeding.
*/
if (!account)
{
xaccTransScrubOrphans (trans);
account = xaccSplitGetAccount (split);
}
/* Grrr... the register gnc_split_register_load() line 203 of
* src/register/ledger-core/split-register-load.c will create
* free-floating bogus transactions. Ignore these for now ...
*/
if (!account)
{
PINFO ("Free Floating Transaction!");
LEAVE ("no account");
return;
}
/* Split amounts and values should be valid numbers */
value = xaccSplitGetValue (split);
if (gnc_numeric_check (value))
{
value = gnc_numeric_zero();
xaccSplitSetValue (split, value);
}
amount = xaccSplitGetAmount (split);
if (gnc_numeric_check (amount))
{
amount = gnc_numeric_zero();
xaccSplitSetAmount (split, amount);
}
currency = xaccTransGetCurrency (trans);
/* If the account doesn't have a commodity,
* we should attempt to fix that first.
*/
acc_commodity = xaccAccountGetCommodity(account);
if (!acc_commodity)
{
xaccAccountScrubCommodity (account);
}
if (!acc_commodity || !gnc_commodity_equiv(acc_commodity, currency))
{
LEAVE ("(split=%p) inequiv currency", split);
return;
}
scu = MIN (xaccAccountGetCommoditySCU (account),
gnc_commodity_get_fraction (currency));
if (gnc_numeric_same (amount, value, scu, GNC_HOW_RND_ROUND_HALF_UP))
{
LEAVE("(split=%p) different values", split);
return;
}
/*
* This will be hit every time you answer yes to the dialog "The
* current transaction has changed. Would you like to record it.
*/
PINFO ("Adjusted split with mismatched values, desc=\"%s\" memo=\"%s\""
" old amount %s %s, new amount %s",
trans->description, split->memo,
gnc_num_dbg_to_string (xaccSplitGetAmount(split)),
gnc_commodity_get_mnemonic (currency),
gnc_num_dbg_to_string (xaccSplitGetValue(split)));
xaccTransBeginEdit (trans);
xaccSplitSetAmount (split, value);
xaccTransCommitEdit (trans);
LEAVE ("(split=%p)", split);
}
| 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 1058 of file Scrub.c.
{
SplitList *node;
gnc_commodity *currency;
if (!trans) return;
/* If there are any orphaned splits in a transaction, then the
* this routine will fail. Therefore, we want to make sure that
* there are no orphans (splits without parent account).
*/
xaccTransScrubOrphans (trans);
currency = xaccTransGetCurrency (trans);
if (currency) return;
currency = xaccTransFindCommonCurrency (trans, qof_instance_get_book(trans));
if (currency)
{
xaccTransBeginEdit (trans);
xaccTransSetCurrency (trans, currency);
xaccTransCommitEdit (trans);
}
else
{
if (NULL == trans->splits)
{
PWARN ("Transaction \"%s\" has no splits in it!", trans->description);
}
else
{
SplitList *node;
char guid_str[GUID_ENCODING_LENGTH + 1];
guid_to_string_buff(xaccTransGetGUID(trans), guid_str);
PWARN ("no common transaction currency found for trans=\"%s\" (%s)",
trans->description, guid_str);
for (node = trans->splits; node; node = node->next)
{
Split *split = node->data;
if (NULL == split->acc)
{
PWARN (" split=\"%s\" is not in any account!", split->memo);
}
else
{
PWARN (" split=\"%s\" account=\"%s\" commodity=\"%s\"",
split->memo, xaccAccountGetName(split->acc),
gnc_commodity_get_mnemonic(xaccAccountGetCommodity(split->acc)));
}
}
}
}
for (node = trans->splits; node; node = node->next)
{
Split *sp = node->data;
if (!gnc_numeric_equal(xaccSplitGetAmount (sp),
xaccSplitGetValue (sp)))
{
gnc_commodity *acc_currency;
acc_currency = sp->acc ? xaccAccountGetCommodity(sp->acc) : NULL;
if (acc_currency == currency)
{
/* This Split needs fixing: The transaction-currency equals
* the account-currency/commodity, but the amount/values are
* inequal i.e. they still correspond to the security
* (amount) and the currency (value). In the new model, the
* value is the amount in the account-commodity -- so it
* needs to be set to equal the amount (since the
* account-currency doesn't exist anymore).
*
* Note: Nevertheless we lose some information here. Namely,
* the information that the 'amount' in 'account-old-security'
* was worth 'value' in 'account-old-currency'. Maybe it would
* be better to store that information in the price database?
* But then, for old currency transactions there is still the
* 'other' transaction, which is going to keep that
* information. So I don't bother with that here. -- cstim,
* 2002/11/20. */
/* But if the commodity *isn't* a currency, then it's
* the value that should be changed to the
* amount. jralls, 2010-11-02 */
PWARN ("Adjusted split with mismatched values, desc=\"%s\" memo=\"%s\""
" old amount %s %s, new amount %s",
trans->description, sp->memo,
gnc_num_dbg_to_string (xaccSplitGetAmount(sp)),
gnc_commodity_get_mnemonic (currency),
gnc_num_dbg_to_string (xaccSplitGetValue(sp)));
xaccTransBeginEdit (trans);
if ( gnc_commodity_is_currency( currency))
{
xaccSplitSetAmount (sp, xaccSplitGetValue(sp));
}
else
{
xaccSplitSetValue(sp, xaccSplitGetAmount(sp));
}
xaccTransCommitEdit (trans);
}
/*else
{
PINFO ("Ok: Split '%s' Amount %s %s, value %s %s",
xaccSplitGetMemo (sp),
gnc_num_dbg_to_string (amount),
gnc_commodity_get_mnemonic (currency),
gnc_num_dbg_to_string (value),
gnc_commodity_get_mnemonic (acc_currency));
}*/
}
}
}
| 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.
{
GList *node;
gnc_commodity *common_currency = NULL;
if (!trans) return;
for (node = xaccTransGetSplitList (trans); node; node = node->next)
{
Split *split = node->data;
if (!xaccTransStillHasSplit(trans, split)) continue;
if (gnc_numeric_equal(xaccSplitGetAmount (split),
xaccSplitGetValue (split)))
{
Account *s_account = xaccSplitGetAccount (split);
gnc_commodity *s_commodity = xaccAccountGetCommodity (s_account);
if (s_commodity)
{
if (gnc_commodity_is_currency(s_commodity))
{
/* Found a split where the amount is the same as the value and
the commodity is a currency. If all splits in the transaction
that fit this description are in the same currency then the
transaction should be in that currency too. */
if (common_currency == NULL)
/* First one we've found, save the currency */
common_currency = s_commodity;
else if ( !gnc_commodity_equiv (common_currency, s_commodity))
{
/* Splits are inconsistent, more than one has a value equal to
the amount, but they aren't all in the same currency. */
common_currency = NULL;
break;
}
}
}
}
}
if (common_currency &&
!gnc_commodity_equiv (common_currency, xaccTransGetCurrency (trans)))
{
/* Found a common currency for the splits, and the transaction is not
in that currency */
gboolean trans_was_open;
PINFO ("transaction in wrong currency");
trans_was_open = xaccTransIsOpen (trans);
if (!trans_was_open)
xaccTransBeginEdit (trans);
xaccTransSetCurrency (trans, common_currency);
if (!trans_was_open)
xaccTransCommitEdit (trans);
}
}
| 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.
{
const gnc_commodity *currency;
if (!trans) return;
ENTER ("()");
/* Must look for orphan splits even if there is no imbalance. */
xaccTransScrubSplits (trans);
/* Return immediately if things are balanced. */
if (xaccTransIsBalanced (trans))
return;
currency = xaccTransGetCurrency (trans);
if (! xaccTransUseTradingAccounts (trans))
{
gnc_numeric imbalance;
/* Make the value sum to zero */
imbalance = xaccTransGetImbalanceValue (trans);
if (! gnc_numeric_zero_p (imbalance))
{
PINFO ("Value unbalanced transaction");
add_balance_split (trans, imbalance, root, account);
}
}
else
{
MonetaryList *imbal_list;
MonetaryList *imbalance_commod;
GList *splits;
gnc_numeric imbalance;
Split *balance_split = NULL;
/* If there are existing trading splits, adjust the price or exchange
rate in each of them to agree with the non-trading splits for the
same commodity. If there are multiple non-trading splits for the
same commodity in the transaction this will use the exchange rate in
the last such split. This shouldn't happen, and if it does then there's
not much we can do about it anyway.
While we're at it, compute the value imbalance ignoring existing
trading splits. */
imbalance = gnc_numeric_zero();
for (splits = trans->splits; splits; splits = splits->next)
{
Split *split = splits->data;
gnc_numeric value, amount;
gnc_commodity *commodity;
if (! xaccTransStillHasSplit (trans, split)) continue;
commodity = xaccAccountGetCommodity (xaccSplitGetAccount(split));
if (!commodity)
{
PERR("Split has no commodity");
continue;
}
balance_split = find_trading_split (trans, root, commodity);
if (balance_split != split)
/* this is not a trading split */
imbalance = gnc_numeric_add(imbalance, xaccSplitGetValue (split),
GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT);
/* Ignore splits where value or amount is zero */
value = xaccSplitGetValue (split);
amount = xaccSplitGetAmount (split);
if (gnc_numeric_zero_p(amount) || gnc_numeric_zero_p(value))
continue;
if (balance_split && balance_split != split)
{
gnc_numeric convrate = gnc_numeric_div (amount, value,
GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE);
gnc_numeric old_value, new_value;
old_value = xaccSplitGetValue(balance_split);
new_value = gnc_numeric_div (xaccSplitGetAmount(balance_split),
convrate,
gnc_commodity_get_fraction(currency),
GNC_HOW_RND_ROUND_HALF_UP);
if (! gnc_numeric_equal (old_value, new_value))
{
xaccTransBeginEdit (trans);
xaccSplitSetValue (balance_split, new_value);
xaccSplitScrub (balance_split);
xaccTransCommitEdit (trans);
}
}
}
/* Balance the value, ignoring existing trading splits */
if (! gnc_numeric_zero_p (imbalance))
{
PINFO ("Value unbalanced transaction");
add_balance_split (trans, imbalance, root, account);
}
/* If the transaction is balanced, nothing more to do */
imbal_list = xaccTransGetImbalance (trans);
if (!imbal_list)
{
LEAVE("()");
return;
}
PINFO ("Currency unbalanced transaction");
for (imbalance_commod = imbal_list; imbalance_commod;
imbalance_commod = imbalance_commod->next)
{
gnc_monetary *imbal_mon = imbalance_commod->data;
gnc_commodity *commodity;
gnc_numeric old_amount, new_amount;
gnc_numeric old_value, new_value, val_imbalance;
GList *splits;
commodity = gnc_monetary_commodity (*imbal_mon);
balance_split = get_trading_split(trans, root, commodity);
if (!balance_split)
{
/* Error already logged */
gnc_monetary_list_free(imbal_list);
LEAVE("");
return;
}
account = xaccSplitGetAccount(balance_split);
if (! gnc_commodity_equal (currency, commodity))
{
/* Find the value imbalance in this commodity */
val_imbalance = gnc_numeric_zero();
for (splits = trans->splits; splits; splits = splits->next)
{
Split *split = splits->data;
if (xaccTransStillHasSplit (trans, split) &&
gnc_commodity_equal (commodity,
xaccAccountGetCommodity(xaccSplitGetAccount(split))))
val_imbalance = gnc_numeric_add (val_imbalance, xaccSplitGetValue (split),
GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT);
}
}
xaccTransBeginEdit (trans);
old_amount = xaccSplitGetAmount (balance_split);
new_amount = gnc_numeric_sub (old_amount, gnc_monetary_value(*imbal_mon),
gnc_commodity_get_fraction(commodity),
GNC_HOW_RND_ROUND_HALF_UP);
xaccSplitSetAmount (balance_split, new_amount);
if (gnc_commodity_equal (currency, commodity))
{
/* Imbalance commodity is the transaction currency, value in the
split must be the same as the amount */
xaccSplitSetValue (balance_split, new_amount);
}
else
{
old_value = xaccSplitGetValue (balance_split);
new_value = gnc_numeric_sub (old_value, val_imbalance,
gnc_commodity_get_fraction(currency),
GNC_HOW_RND_ROUND_HALF_UP);
xaccSplitSetValue (balance_split, new_value);
}
xaccSplitScrub (balance_split);
xaccTransCommitEdit (trans);
}
gnc_monetary_list_free(imbal_list);
if (!gnc_numeric_zero_p(xaccTransGetImbalanceValue(trans)))
{
/* This is probably because there are splits with zero amount
and non-zero value. These are usually realized gain/loss
splits. Add a reversing split for each of them to balance
the value. */
/* Copy the split list so we don't see the splits we're adding */
GList *splits_dup = g_list_copy(trans->splits);
for (splits = splits_dup; splits; splits = splits->next)
{
Split *split = splits->data;
if (! xaccTransStillHasSplit(trans, split)) continue;
if (!gnc_numeric_zero_p(xaccSplitGetValue(split)) &&
gnc_numeric_zero_p(xaccSplitGetAmount(split)))
{
gnc_commodity *commodity;
gnc_numeric old_value, new_value;
commodity = xaccAccountGetCommodity(xaccSplitGetAccount(split));
if (!commodity)
{
PERR("Split has no commodity");
continue;
}
balance_split = get_trading_split(trans, root, commodity);
if (!balance_split)
{
/* Error already logged */
gnc_monetary_list_free(imbal_list);
LEAVE("");
return;
}
account = xaccSplitGetAccount(balance_split);
xaccTransBeginEdit (trans);
old_value = xaccSplitGetValue (balance_split);
new_value = gnc_numeric_sub (old_value, xaccSplitGetValue(split),
gnc_commodity_get_fraction(currency),
GNC_HOW_RND_ROUND_HALF_UP);
xaccSplitSetValue (balance_split, new_value);
/* Don't change the balance split's amount since the amount
is zero in the split we're working on */
xaccSplitScrub (balance_split);
xaccTransCommitEdit (trans);
}
}
g_list_free(splits_dup);
if (!gnc_numeric_zero_p(xaccTransGetImbalanceValue(trans)))
PERR("Balancing currencies unbalanced value");
}
}
LEAVE ("()");
}
| void xaccTransScrubOrphans | ( | Transaction * | trans | ) |
The xaccTransScrubOrphans() method scrubs only the splits in the given transaction.
Definition at line 124 of file Scrub.c.
{
SplitList *node;
QofBook *book = NULL;
Account *root = NULL;
for (node = trans->splits; node; node = node->next)
{
Split *split = node->data;
if (split->acc)
{
TransScrubOrphansFast (trans, gnc_account_get_root(split->acc));
return;
}
}
/* If we got to here, then *none* of the splits belonged to an
* account. Not a happy situation. We should dig an account
* out of the book the transaction belongs to.
* XXX we should probably *always* to this, instead of the above loop!
*/
PINFO ("Free Floating Transaction!");
book = xaccTransGetBook (trans);
root = gnc_book_get_root_account (book);
TransScrubOrphansFast (trans, root);
}
| 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 2173 of file Transaction.c.
{
gnc_commodity *currency;
if (!trans) return;
xaccTransBeginEdit(trans);
/* The split scrub expects the transaction to have a currency! */
currency = xaccTransGetCurrency (trans);
if (!currency)
PERR ("Transaction doesn't have a currency!");
FOR_EACH_SPLIT(trans, xaccSplitScrub(s));
xaccTransCommitEdit(trans);
}
1.7.4