コード例 #1
0
ファイル: gnc-tree-util-split-reg.c プロジェクト: 573/gnucash
/* Is current split a security account */
static gboolean
gtu_sr_use_security (GncTreeViewSplitReg *view)
{
    RowDepth depth;
    Account *account = NULL;
    Split *split;

    split = gnc_tree_view_split_reg_get_current_split (view);

    depth = gnc_tree_view_reg_get_selected_row_depth (view);

    if (!split)
        return TRUE;

    if (depth != SPLIT3)
        return TRUE;

    if (!account)
        account = xaccSplitGetAccount (split);

    if (!account)
        return TRUE;

    if (xaccTransUseTradingAccounts (xaccSplitGetParent (split)))
    {
        if (!gnc_commodity_is_iso (xaccAccountGetCommodity (account)))
            return TRUE;
    }

    return xaccAccountIsPriced (account);
}
コード例 #2
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");

}
コード例 #3
0
ファイル: gnc-tree-util-split-reg.c プロジェクト: 573/gnucash
/* Get the rate */
gnc_numeric
gnc_tree_util_get_rate_for (GncTreeViewSplitReg *view, Transaction *trans, Split *split, gboolean is_blank)
{
    gnc_numeric num;

    ENTER("trans %p and split %p is_blank %d", trans, split, is_blank);

    num = gnc_tree_util_split_reg_get_value_for (view, trans, split, is_blank);
//FIXME Not sure about this...
    if (xaccTransUseTradingAccounts (trans))
        num = gnc_numeric_div (num, xaccSplitGetValue (split), GNC_DENOM_AUTO, GNC_HOW_RND_ROUND);
    else
        num = gnc_numeric_div (xaccSplitGetAmount (split), num, GNC_DENOM_AUTO, GNC_HOW_RND_ROUND);

    LEAVE("split %p and return num is %s", split, gnc_numeric_to_string (num));
    return num;
}
コード例 #4
0
static void
gnc_split_register_save_amount_values (SRSaveData *sd, SplitRegister *reg)
{
    Account *acc;
    gnc_numeric new_amount, convrate, amtconv, value;
    gnc_commodity *curr, *reg_com, *xfer_com;
    Account *xfer_acc;

    new_amount = gnc_split_register_debcred_cell_value (reg);
    acc = gnc_split_register_get_default_account (reg);

    xfer_acc = xaccSplitGetAccount (sd->split);
    xfer_com = xaccAccountGetCommodity (xfer_acc);
    reg_com = xaccAccountGetCommodity (acc);
    curr = xaccTransGetCurrency (sd->trans);

    /* First, compute the conversion rate to convert the value to the
      * amount.
      */
    amtconv = convrate = gnc_split_register_get_rate_cell (reg, RATE_CELL);
    if (acc && gnc_split_register_needs_conv_rate (reg, sd->trans, acc))
    {

        /* If we are in an expanded register and the xfer_acc->comm !=
        * reg_acc->comm then we need to compute the convrate here.
        * Otherwise, we _can_ use the rate_cell!
        */
        if (sd->reg_expanded && ! gnc_commodity_equal (reg_com, xfer_com))
            amtconv = xaccTransGetAccountConvRate(sd->trans, acc);
    }

    if (xaccTransUseTradingAccounts (sd->trans))
    {
        /* Using currency accounts, the amount is probably really the
           amount and not the value. */
        gboolean is_amount;
        if (reg->type == STOCK_REGISTER ||
                reg->type == CURRENCY_REGISTER ||
                reg->type == PORTFOLIO_LEDGER)
        {
            if (xaccAccountIsPriced(xfer_acc) ||
                    !gnc_commodity_is_iso(xaccAccountGetCommodity(xfer_acc)))
                is_amount = FALSE;
            else
                is_amount = TRUE;
        }
        else
        {
            is_amount = TRUE;
        }

        if (is_amount)
        {
            xaccSplitSetAmount(sd->split, new_amount);
            if (gnc_split_register_split_needs_amount (reg, sd->split))
            {
                value = gnc_numeric_div(new_amount, amtconv,
                                        gnc_commodity_get_fraction(curr),
                                        GNC_HOW_RND_ROUND_HALF_UP);
                xaccSplitSetValue(sd->split, value);
            }
            else
                xaccSplitSetValue(sd->split, new_amount);
        }
        else
        {
            xaccSplitSetValue(sd->split, new_amount);
        }

        return;
    }

    /* How to interpret new_amount depends on our view of this
     * transaction.  If we're sitting in an account with the same
     * commodity as the transaction, then we can set the Value and then
     * compute the amount.  Otherwise we are setting the "converted
     * value".  This means we need to convert new_amount to the actual
     * 'value' by dividing by the convrate in order to set the value.
     */

    /* Now compute/set the split value.  Amount is in the register
     * currency but we need to convert to the txn currency.
     */
    if (gnc_split_register_needs_conv_rate (reg, sd->trans, acc))
    {

        /* convert the amount to the Value ... */
        value = gnc_numeric_div (new_amount, amtconv,
                                 gnc_commodity_get_fraction (curr),
                                 GNC_HOW_RND_ROUND_HALF_UP);
        xaccSplitSetValue (sd->split, value);
    }
    else
        xaccSplitSetValue (sd->split, new_amount);

    /* Now re-compute the Amount from the Value.  We may need to convert
     * from the Value back to the amount here using the convrate from
     * earlier.
     */
    value = xaccSplitGetValue (sd->split);

    if (gnc_split_register_split_needs_amount (reg, sd->split))
    {
        acc = xaccSplitGetAccount (sd->split);
        new_amount = gnc_numeric_mul (value, convrate,
                                      xaccAccountGetCommoditySCU (acc),
                                      GNC_HOW_RND_ROUND_HALF_UP);
        xaccSplitSetAmount (sd->split, new_amount);
    }
}
コード例 #5
0
ファイル: gnc-tree-util-split-reg.c プロジェクト: 573/gnucash
void
gnc_tree_util_split_reg_save_amount_values (GncTreeViewSplitReg *view, Transaction *trans, Split *split, gnc_numeric input)
{
    GncTreeModelSplitReg *model;
    Account *acc;
    gnc_numeric new_amount, convrate, amtconv, value;
    gnc_commodity *curr, *reg_com, *xfer_com;
    Account *xfer_acc;

    ENTER("View is %p, trans is %p, split is %p, input is %s", view, trans, split, gnc_numeric_to_string (input));

    model = gnc_tree_view_split_reg_get_model_from_view (view);

    new_amount = input;

    acc = gnc_tree_model_split_reg_get_anchor (model);

    xfer_acc = xaccSplitGetAccount (split);
    xfer_com = xaccAccountGetCommodity (xfer_acc);
    reg_com = xaccAccountGetCommodity (acc);
    curr = xaccTransGetCurrency (trans);

    if (!xaccTransGetRateForCommodity (trans, reg_com, NULL, &convrate))
        convrate = gnc_numeric_create (100, 100);

    amtconv = convrate;

    if (gnc_tree_util_split_reg_needs_conv_rate (view, trans, acc))
    {

        /* If we are in an expanded register and the xfer_acc->comm !=
        * reg_acc->comm then we need to compute the convrate here.
        * Otherwise, we _can_ use the rate_cell!
        */
        if (gnc_commodity_equal (reg_com, xfer_com))
            amtconv = xaccTransGetAccountConvRate (trans, acc);
    }

    if (xaccTransUseTradingAccounts (trans))
    {
        /* Using currency accounts, the amount is probably really the
           amount and not the value. */
        gboolean is_amount;

        if (model->type == STOCK_REGISTER2 ||
                model->type == CURRENCY_REGISTER2 ||
                model->type == PORTFOLIO_LEDGER2)
        {
            if (xaccAccountIsPriced (xfer_acc) ||
                    !gnc_commodity_is_iso (xaccAccountGetCommodity (xfer_acc)))
                is_amount = FALSE;
            else
                is_amount = TRUE;
        }
        else
        {
            is_amount = TRUE;
        }

        if (is_amount)
        {
            xaccSplitSetAmount (split, new_amount);
            if (gnc_tree_util_split_reg_needs_amount (view, split))
            {
                value = gnc_numeric_div (new_amount, amtconv,
                                        gnc_commodity_get_fraction (curr),
                                        GNC_HOW_RND_ROUND_HALF_UP);
                xaccSplitSetValue (split, value);
            }
            else
                xaccSplitSetValue (split, new_amount);
        }
        else
        {
            xaccSplitSetValue (split, new_amount);
        }
        LEAVE(" ");
        return;
    }

    /* How to interpret new_amount depends on our view of this
     * transaction.  If we're sitting in an account with the same
     * commodity as the transaction, then we can set the Value and then
     * compute the amount.  Otherwise we are setting the "converted
     * value".  This means we need to convert new_amount to the actual
     * 'value' by dividing by the convrate in order to set the value.
     */

    /* Now compute/set the split value.  Amount is in the register
     * currency but we need to convert to the txn currency.
     */
    if (gnc_tree_util_split_reg_needs_conv_rate (view, trans, acc))
    {
        /* convert the amount to the Value ... */
        value = gnc_numeric_div (new_amount, amtconv,
                                 gnc_commodity_get_fraction (curr),
                                 GNC_HOW_RND_ROUND_HALF_UP);
        xaccSplitSetValue (split, value);
    }
    else
    {
        xaccSplitSetValue (split, new_amount);
    }

    /* Now re-compute the Amount from the Value.  We may need to convert
     * from the Value back to the amount here using the convrate from
     * earlier.
     */
    value = xaccSplitGetValue (split);

    if (gnc_tree_util_split_reg_needs_amount (view, split))
    {
        acc = xaccSplitGetAccount (split);
        new_amount = gnc_numeric_mul (value, convrate,
                                      xaccAccountGetCommoditySCU (acc),
                                      GNC_HOW_RND_ROUND_HALF_UP);
        xaccSplitSetAmount (split, new_amount);
    }
    else
    {
        xaccSplitSetAmount (split, value);
    }
    LEAVE(" ");
}
コード例 #6
0
ファイル: gnc-tree-util-split-reg.c プロジェクト: 573/gnucash
gboolean
gnc_tree_util_split_reg_get_debcred_entry (GncTreeViewSplitReg *view,
                                      Transaction *trans, Split *split,
                                      gboolean is_blank, gnc_numeric *ret_num,
                                      GNCPrintAmountInfo *ret_print_info)

{
    GncTreeModelSplitReg *model;
    gnc_commodity *currency;

    model = gnc_tree_view_split_reg_get_model_from_view (view);

    currency = xaccTransGetCurrency (trans);
    if (!currency)
        currency = gnc_default_currency ();

    if (is_blank)
    {
        gnc_numeric imbalance;
        Account *acc;

        imbalance = xaccTransGetImbalanceValue (trans);

        if (gnc_numeric_zero_p (imbalance))
            return FALSE;

        if (xaccTransUseTradingAccounts (trans))
        {
            MonetaryList *imbal_list;
            gnc_monetary *imbal_mon;
            imbal_list = xaccTransGetImbalance (trans);

            if (!imbal_list)
            {
                /* No commodity imbalance, there shouldn't be a value imablance. */
                return FALSE;
            }

            if (imbal_list->next)
            {
                /* Multiple currency imbalance. */
                gnc_monetary_list_free (imbal_list);
                return FALSE;
            }

            imbal_mon = imbal_list->data;
            if (!gnc_commodity_equal (gnc_monetary_commodity (*imbal_mon), currency))
            {
                /* Imbalance is in wrong currency */
                gnc_monetary_list_free (imbal_list);
                return FALSE;
            }

            if (!gnc_numeric_equal (gnc_monetary_value (*imbal_mon), imbalance))
            {
                /* Value and commodity imbalances differ */
                gnc_monetary_list_free (imbal_list);
                return FALSE;
            }

            /* Done with the imbalance list */
            gnc_monetary_list_free (imbal_list);
        }

        imbalance = gnc_numeric_neg (imbalance);

        acc = gnc_tree_model_split_reg_get_anchor (model);

        if (gnc_tree_util_split_reg_needs_conv_rate (view, trans, acc))
        {
            imbalance = gnc_numeric_mul (imbalance,
                                         xaccTransGetAccountConvRate (trans, acc),
                                         gnc_commodity_get_fraction (currency),
                                         GNC_HOW_RND_ROUND_HALF_UP);
        }
        else
        {
            imbalance = gnc_numeric_convert (imbalance,
                                             gnc_commodity_get_fraction (currency),
                                             GNC_HOW_RND_ROUND_HALF_UP);
        }

        *ret_num = imbalance;
        *ret_print_info = gnc_account_print_info (acc, FALSE);
         return TRUE;
    }

    {
        gnc_numeric amount;
        gnc_commodity *split_commodity;
        GNCPrintAmountInfo print_info;
        Account *account;
        gnc_commodity *commodity;

        account = gnc_tree_model_split_reg_get_anchor (model);

        commodity = xaccAccountGetCommodity (account);
        split_commodity = xaccAccountGetCommodity (xaccSplitGetAccount (split));

        if (xaccTransUseTradingAccounts (trans))
        {
            gboolean use_symbol, is_current = FALSE;
            Split *current_split = gnc_tree_view_split_reg_get_current_split (view);
            RowDepth depth = gnc_tree_view_reg_get_selected_row_depth (view);

            if ((split == current_split) && (depth == SPLIT3))
                is_current = TRUE;

            if (model->type == STOCK_REGISTER2 ||
                    model->type == CURRENCY_REGISTER2 ||
                    model->type == PORTFOLIO_LEDGER2)
            {
                gnc_commodity *amount_commodity;
                /* security register.  If this split has price and shares columns,
                   use the value, otherwise use the amount.  */
                if (gtu_sr_use_security (view))
                {
                    amount = xaccSplitGetValue (split);
                    amount_commodity = currency;
                }
                else
                {
                    amount = xaccSplitGetAmount (split);
                    amount_commodity = split_commodity;
                }
                /* Show the currency if it is not the default currency */
                if (is_current ||
                        gnc_commodity_equiv (amount_commodity, gnc_default_currency ()))
                    use_symbol = FALSE;
                else
                    use_symbol = TRUE;
                print_info = gnc_commodity_print_info (amount_commodity, use_symbol);
            }
            else
            {
                /* non-security register, always use the split amount. */
                amount = xaccSplitGetAmount (split);
                if (is_current ||
                        gnc_commodity_equiv (split_commodity, commodity))
                    use_symbol = FALSE;
                else
                    use_symbol = TRUE;
                print_info = gnc_commodity_print_info (split_commodity, use_symbol);
            }
        }
        else
        {
            /* If this account is not a stock/mutual/currency account, and
            * currency != the account commodity, then use the SplitAmount
            * instead of the SplitValue.
            */
            switch (model->type)
            {
            case STOCK_REGISTER2:
            case CURRENCY_REGISTER2:
            case PORTFOLIO_LEDGER2:
                amount = xaccSplitGetValue (split);
                print_info = gnc_commodity_print_info (currency, FALSE);
                break;

            default:
                if (commodity && !gnc_commodity_equal (commodity, currency))
                    /* Convert this to the "local" value */
                    amount = xaccSplitConvertAmount (split, account);
                else
                    amount = xaccSplitGetValue (split);
                print_info = gnc_account_print_info (account, FALSE);
                break;
            }
        }

        if (gnc_numeric_zero_p (amount))
            return FALSE;

        *ret_num = amount;
        *ret_print_info = print_info;
         return TRUE;
    }
}
コード例 #7
0
ファイル: gnc-tree-util-split-reg.c プロジェクト: 573/gnucash
/* Takes the input with column and sets the price / amount / value so they are consistent */
void
gnc_tree_util_set_number_for_input (GncTreeViewSplitReg *view, Transaction *trans, Split *split, gnc_numeric input, gint viewcol)
{
    GncTreeModelSplitReg *model;
    gnc_numeric  price;
    gnc_numeric  amount;
    gnc_numeric  value;

    gboolean price_changed = FALSE;   // Price of each share
    gboolean value_changed = FALSE;   // Total value of shares
    gboolean amount_changed = FALSE;  // No of shares

    gboolean recalc_amount = FALSE;
    gboolean recalc_price = FALSE;
    gboolean recalc_value = FALSE;
    gboolean expanded = FALSE;
    int denom;
    Account *account = NULL;

    ENTER("trans %p and split %p and input is %s and viewcol is %d", trans, split, gnc_numeric_to_string (input), viewcol);

    model = gnc_tree_view_split_reg_get_model_from_view (view);

    /* Check for sub account view */
    if (!gnc_tree_model_split_reg_get_sub_account (model))
        account = gnc_tree_model_split_reg_get_anchor (model);

    expanded = gnc_tree_view_split_reg_trans_expanded (view, trans);

    if (!account)
        account = xaccSplitGetAccount (split);

    if (!xaccAccountIsPriced (account))
        return;

    /* If we are using commodity trading accounts then the value may
       not really be the value.  Punt if so. */
    if (xaccTransUseTradingAccounts (xaccSplitGetParent (split)))
    {
        gnc_commodity *acc_commodity;
        acc_commodity = xaccAccountGetCommodity (account);
        if (!(xaccAccountIsPriced (account) || !gnc_commodity_is_iso (acc_commodity)))
            return;
    }

    if (gnc_numeric_zero_p (input))
    {
        xaccSplitSetValue (split, input);
        xaccSplitSetAmount (split, input);
        LEAVE("zero");
        return;
    }

    amount = xaccSplitGetAmount (split);
    value = xaccSplitGetValue (split);

    if (viewcol == COL_AMTVAL && !expanded)
    {
        value_changed = TRUE;
        if (gnc_numeric_zero_p (amount))
        {
            xaccSplitSetValue (split, input);
            xaccSplitSetAmount (split, input);
            LEAVE("");
            return;
        }
    }
    else if (viewcol == COL_AMTVAL && expanded)
    {
        amount_changed = TRUE;
        if (gnc_numeric_zero_p (value))
        {
            xaccSplitSetValue (split, input);
            xaccSplitSetAmount (split, input);
            LEAVE("");
            return;
        }
    }

    if (viewcol == COL_PRICE)
    {
        price_changed = TRUE;
        if (gnc_numeric_zero_p (value))
        {
            amount = gnc_numeric_create (1,1);
            value = gnc_numeric_mul (input, amount, GNC_DENOM_AUTO, GNC_HOW_RND_ROUND);
            xaccSplitSetValue (split, input);
            xaccSplitSetAmount (split, amount);
            LEAVE("");
            return;
        }
    }

    if ((viewcol == COL_CREDIT || viewcol == COL_DEBIT) && !expanded)
    {
        amount_changed = TRUE;
        if (gnc_numeric_zero_p (value))
        {
            xaccSplitSetValue (split, input);
            xaccSplitSetAmount (split, input);
            LEAVE("");
            return;
        }
    }
    else if ((viewcol == COL_CREDIT || viewcol == COL_DEBIT) && expanded)
    {
        value_changed = TRUE;
        if (gnc_numeric_zero_p (value))
        {
            xaccSplitSetValue (split, input);
            xaccSplitSetAmount (split, input);
            LEAVE("");
            return;
        }
    }

    DEBUG("value_changed %d, price_changed %d, amount_changed %d", value_changed, price_changed, amount_changed);

    {
        int choice;
        int default_value;
        GList *node;
        GList *radio_list = NULL;
        const char *title = _("Recalculate Transaction");
        const char *message = _("The values entered for this transaction "
                                "are inconsistent. Which value would you "
                                "like to have recalculated?");

        if (amount_changed)
            radio_list = g_list_append (radio_list,
                                        g_strdup_printf ("%s (%s)",
                                                _("_Shares"), _("Changed")));
        else
            radio_list = g_list_append (radio_list, g_strdup (_("_Shares")));

        if (price_changed)
            radio_list = g_list_append (radio_list,
                                        g_strdup_printf ("%s (%s)",
                                                _("_Price"), _("Changed")));
        else
            radio_list = g_list_append (radio_list, g_strdup (_("_Price")));

        if (value_changed)
            radio_list = g_list_append (radio_list,
                                        g_strdup_printf ("%s (%s)",
                                                _("_Value"), _("Changed")));
        else
            radio_list = g_list_append (radio_list, g_strdup (_("_Value")));

        if(expanded)
        {
            if (price_changed)
                default_value = 2;  /* change the value */
            else
                default_value = 1;  /* change the price */
        }
        else
        {
            if (price_changed)
                default_value = 0;  /* change the amount / shares */
            else
                default_value = 1;  /* change the price */
        }
        choice = gnc_choose_radio_option_dialog
                 (gnc_tree_view_split_reg_get_parent (view),
                  title,
                  message,
                  _("_Recalculate"),
                  default_value,
                  radio_list);

        for (node = radio_list; node; node = node->next)
            g_free (node->data);

        g_list_free (radio_list);

        switch (choice)
        {
        case 0: /* Modify number of shares */
            recalc_amount = TRUE;
            break;
        case 1: /* Modify the share price */
            recalc_price = TRUE;
            break;
        case 2: /* Modify total value */
            recalc_value = TRUE;
            break;
        default: /* Cancel */
            LEAVE(" " );
            return;
        }
    }

    DEBUG("recalc_value %d, recalc_price %d, recalc_amount %d", recalc_value, recalc_price, recalc_amount);

    if (recalc_amount)
    {
        denom = gtu_sr_get_amount_denom (split);

        if (amount_changed)
        {
            LEAVE("");
            return;
        }

        if (price_changed)
            price = input;
        else
            price = gnc_numeric_div (value, amount, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT);

        if (value_changed)
        {
            xaccSplitSetValue (split, input);
            amount = gnc_numeric_div (input, price, denom, GNC_HOW_RND_ROUND_HALF_UP);
            xaccSplitSetAmount (split, amount);
        }
        else
        {
            amount = gnc_numeric_div (value, price, denom, GNC_HOW_RND_ROUND_HALF_UP);
            xaccSplitSetAmount (split, amount);
        }
    }

    if (recalc_price)
    {
        if (price_changed)
        {
            LEAVE("");
            return;
        }

        if (amount_changed)
        {
            xaccSplitSetAmount (split, input);
            xaccSplitSetValue (split, value);
        }

        if (value_changed)
        {
            xaccSplitSetValue (split, input);
            xaccSplitSetAmount (split, amount);
        }
    }

    if (recalc_value)
    {
        denom = gtu_sr_get_value_denom (split);

        if (value_changed)
        {
            LEAVE("");
            return;
        }

        if (price_changed)
            price = input;
        else
            price = gnc_numeric_div (value, amount, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT);

        if (amount_changed)
        {
            xaccSplitSetAmount (split, input);
            value = gnc_numeric_mul (input, price, denom, GNC_HOW_RND_ROUND_HALF_UP);
            xaccSplitSetValue (split, value);
        }
        else
        {
            value = gnc_numeric_mul (amount, price, denom, GNC_HOW_RND_ROUND_HALF_UP);
            xaccSplitSetValue (split, value);
        }
    }

    /* If the number of splits is two, change other split to balance */
    if (!gnc_tree_util_split_reg_is_multi (split) && expanded)
    {
        Split *osplit;
        gnc_commodity *osplit_com;

        osplit = xaccSplitGetOtherSplit (split);

        value = xaccSplitGetValue (split);

        osplit_com = xaccAccountGetCommodity (xaccSplitGetAccount (osplit));

        if (gnc_commodity_is_currency (osplit_com))
        {
            xaccSplitSetValue (osplit, gnc_numeric_neg (value));
            xaccSplitSetAmount (osplit, gnc_numeric_neg (value));
        }
    }
    LEAVE("");
}
コード例 #8
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 ("()");
}