Some extract-function refactors to xaccTransScrubImbalance.

This commit is contained in:
John Ralls 2015-10-11 21:49:53 -07:00
parent 734ecce36d
commit 61973a8302

View File

@ -527,50 +527,23 @@ add_balance_split (Transaction *trans, gnc_numeric imbalance,
xaccTransCommitEdit (trans);
}
void
xaccTransScrubImbalance (Transaction *trans, Account *root,
/* Balance a transaction without trading accounts. */
static void
gnc_transaction_balance_no_trading (Transaction *trans, Account *root,
Account *account)
{
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))
{
LEAVE ("transaction is balanced");
return;
}
currency = xaccTransGetCurrency (trans);
if (! xaccTransUseTradingAccounts (trans))
{
gnc_numeric imbalance;
gnc_numeric imbalance = xaccTransGetImbalanceValue (trans);
/* 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
}
/** 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
@ -580,13 +553,17 @@ xaccTransScrubImbalance (Transaction *trans, Account *root,
While we're at it, compute the value imbalance ignoring existing
trading splits. */
imbalance = gnc_numeric_zero();
static gnc_numeric
gnc_transaction_adjust_trading_splits (Transaction* trans, Account *root)
{
GList* splits;
gnc_numeric imbalance = gnc_numeric_zero();
for (splits = trans->splits; splits; splits = splits->next)
{
Split *split = splits->data;
Split *balance_split = NULL;
gnc_numeric value, amount;
gnc_commodity *commodity;
gnc_commodity *commodity, *txn_curr = xaccTransGetCurrency (trans);
if (! xaccTransStillHasSplit (trans, split)) continue;
@ -618,7 +595,7 @@ xaccTransScrubImbalance (Transaction *trans, Account *root,
old_value = xaccSplitGetValue(balance_split);
new_value = gnc_numeric_div (xaccSplitGetAmount(balance_split),
convrate,
gnc_commodity_get_fraction(currency),
gnc_commodity_get_fraction(txn_curr),
GNC_HOW_RND_ROUND_HALF_UP);
if (! gnc_numeric_equal (old_value, new_value))
{
@ -629,15 +606,38 @@ xaccTransScrubImbalance (Transaction *trans, Account *root,
}
}
}
/* Balance the value, ignoring existing trading splits */
if (! gnc_numeric_zero_p (imbalance))
{
PINFO ("Value unbalanced transaction");
add_balance_split (trans, imbalance, root, account);
return imbalance;
}
static gnc_numeric
gnc_transaction_get_commodity_imbalance (Transaction *trans,
gnc_commodity *commodity)
{
/* Find the value imbalance in this commodity */
gnc_numeric val_imbalance = gnc_numeric_zero();
GList *splits = NULL;
for (splits = trans->splits; splits; splits = splits->next)
{
Split *split = splits->data;
gnc_commodity *split_commodity =
xaccAccountGetCommodity(xaccSplitGetAccount(split));
if (xaccTransStillHasSplit (trans, split) &&
gnc_commodity_equal (commodity, split_commodity))
val_imbalance = gnc_numeric_add (val_imbalance,
xaccSplitGetValue (split),
GNC_DENOM_AUTO,
GNC_HOW_DENOM_EXACT);
}
return val_imbalance;
}
static void
gnc_transaction_balance_trading (Transaction *trans, Account *root)
{
MonetaryList *imbal_list;
MonetaryList *imbalance_commod;
Split *balance_split = NULL;
/* If the transaction is balanced, nothing more to do */
imbal_list = xaccTransGetImbalance (trans);
if (!imbal_list)
@ -655,7 +655,8 @@ xaccTransScrubImbalance (Transaction *trans, Account *root,
gnc_commodity *commodity;
gnc_numeric old_amount, new_amount;
gnc_numeric old_value, new_value, val_imbalance;
GList *splits;
Account *account = NULL;
const gnc_commodity *txn_curr = xaccTransGetCurrency (trans);
commodity = gnc_monetary_commodity (*imbal_mon);
@ -670,19 +671,9 @@ xaccTransScrubImbalance (Transaction *trans, Account *root,
account = xaccSplitGetAccount(balance_split);
if (! gnc_commodity_equal (currency, commodity))
if (! gnc_commodity_equal (txn_curr, 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);
}
gnc_transaction_get_commodity_imbalance (trans, commodity);
}
xaccTransBeginEdit (trans);
@ -694,7 +685,7 @@ xaccTransScrubImbalance (Transaction *trans, Account *root,
xaccSplitSetAmount (balance_split, new_amount);
if (gnc_commodity_equal (currency, commodity))
if (gnc_commodity_equal (txn_curr, commodity))
{
/* Imbalance commodity is the transaction currency, value in the
split must be the same as the amount */
@ -704,7 +695,7 @@ xaccTransScrubImbalance (Transaction *trans, Account *root,
{
old_value = xaccSplitGetValue (balance_split);
new_value = gnc_numeric_sub (old_value, val_imbalance,
gnc_commodity_get_fraction(currency),
gnc_commodity_get_fraction(txn_curr),
GNC_HOW_RND_ROUND_HALF_UP);
xaccSplitSetValue (balance_split, new_value);
@ -715,16 +706,19 @@ xaccTransScrubImbalance (Transaction *trans, Account *root,
}
gnc_monetary_list_free(imbal_list);
}
if (!gnc_numeric_zero_p(xaccTransGetImbalanceValue(trans)))
/** Balance the transaction by adding more trading splits. This shouldn't
* ordinarily be necessary.
* @param trans the transaction to balance
* @param root the root account
*/
static void
gnc_transaction_balance_trading_more_splits (Transaction *trans, Account *root)
{
/* 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);
GList *splits_dup = g_list_copy(trans->splits), *splits = NULL;
const gnc_commodity *txn_curr = xaccTransGetCurrency (trans);
for (splits = splits_dup; splits; splits = splits->next)
{
Split *split = splits->data;
@ -734,6 +728,8 @@ xaccTransScrubImbalance (Transaction *trans, Account *root,
{
gnc_commodity *commodity;
gnc_numeric old_value, new_value;
Split *balance_split;
Account *account = NULL;
commodity = xaccAccountGetCommodity(xaccSplitGetAccount(split));
if (!commodity)
@ -745,7 +741,6 @@ xaccTransScrubImbalance (Transaction *trans, Account *root,
if (!balance_split)
{
/* Error already logged */
gnc_monetary_list_free(imbal_list);
LEAVE("");
return;
}
@ -755,7 +750,7 @@ xaccTransScrubImbalance (Transaction *trans, Account *root,
old_value = xaccSplitGetValue (balance_split);
new_value = gnc_numeric_sub (old_value, xaccSplitGetValue(split),
gnc_commodity_get_fraction(currency),
gnc_commodity_get_fraction(txn_curr),
GNC_HOW_RND_ROUND_HALF_UP);
xaccSplitSetValue (balance_split, new_value);
@ -768,12 +763,66 @@ xaccTransScrubImbalance (Transaction *trans, Account *root,
}
g_list_free(splits_dup);
}
/** Correct transaction imbalances.
* @param trans The Transaction
* @param root The (hidden) root account, for the book default currency.
* @param account The account whose currency in which to balance.
*/
void
xaccTransScrubImbalance (Transaction *trans, Account *root,
Account *account)
{
gnc_numeric imbalance;
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))
{
LEAVE ("transaction is balanced");
return;
}
if (! xaccTransUseTradingAccounts (trans))
{
gnc_transaction_balance_no_trading (trans, root, account);
LEAVE ("transaction balanced, no trading accounts");
return;
}
imbalance = gnc_transaction_adjust_trading_splits (trans, root);
/* Balance the value, ignoring existing trading splits */
if (! gnc_numeric_zero_p (imbalance))
{
PINFO ("Value unbalanced transaction");
add_balance_split (trans, imbalance, root, account);
}
gnc_transaction_balance_trading (trans, root);
if (gnc_numeric_zero_p(xaccTransGetImbalanceValue(trans)))
{
LEAVE ("()");
return;
}
/* If the transaction is still not balanced, it's 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. */
gnc_transaction_balance_trading_more_splits (trans, root);
if (!gnc_numeric_zero_p(xaccTransGetImbalanceValue(trans)))
PERR("Balancing currencies unbalanced value");
}
}
LEAVE ("()");
}
/* ================================================================ */