예제 #1
0
파일: Scrub.c 프로젝트: Bob-IT/gnucash
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");

}
예제 #2
0
파일: Scrub.c 프로젝트: Bob-IT/gnucash
/* Balance a transaction without trading accounts. */
static void
gnc_transaction_balance_no_trading (Transaction *trans, Account *root,
                                    Account *account)
{
    gnc_numeric imbalance  = xaccTransGetImbalanceValue (trans);

    /* Make the value sum to zero */
    if (! gnc_numeric_zero_p (imbalance))
    {
        PINFO ("Value unbalanced transaction");

        add_balance_split (trans, imbalance, root, account);
    }

}
예제 #3
0
파일: Scrub.c 프로젝트: BenBergman/gnucash
void
xaccTransScrubImbalance (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;

        /* 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("transaction is balanced");
            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 ("()");
}