Пример #1
0
static void
calculate_selected_total_helper (GtkTreeModel *model,
                                 GtkTreePath *path,
                                 GtkTreeIter *iter,
                                 gpointer data)
{
    gnc_numeric *subtotal = (gnc_numeric*) data;
    gnc_numeric cur_val;
    GValue value = { 0 };
    GNCLot *lot;
    Account *acct;
    gnc_commodity *currency;

    gtk_tree_model_get_value (model, iter, 5, &value);
    lot = (GNCLot *) g_value_get_pointer (&value);
    g_value_unset (&value);

    /* Find the amount's currency to determine the required precision */
    acct = gnc_lot_get_account (lot);
    currency = xaccAccountGetCommodity (acct);

    cur_val = gnc_lot_get_balance (lot);
    *subtotal = gnc_numeric_add (*subtotal, cur_val,
                                 gnc_commodity_get_fraction (currency), GNC_HOW_RND_ROUND_HALF_UP);
}
Пример #2
0
/*
 * This will add value to the account-value for acc, creating a new
 * list object if necessary
 */
GList *gncAccountValueAdd (GList *list, Account *acc, gnc_numeric value)
{
    GList *li;
    GncAccountValue *res = NULL;

    g_return_val_if_fail (acc, list);
    g_return_val_if_fail (gnc_numeric_check (value) == GNC_ERROR_OK, list);

    /* Try to find the account in the list */
    for (li = list; li; li = li->next)
    {
        res = li->data;
        if (res->account == acc)
        {
            res->value = gnc_numeric_add (res->value, value, GNC_DENOM_AUTO,
                                          GNC_HOW_DENOM_LCD);
            return list;
        }
    }
    /* Nope, didn't find it. */

    res = g_new0 (GncAccountValue, 1);
    res->account = acc;
    res->value = value;
    return g_list_prepend (list, res);
}
Пример #3
0
gnc_numeric
gnc_numeric_add_with_error(gnc_numeric a, gnc_numeric b,
                           gint64 denom, gint how,
                           gnc_numeric * error)
{

    gnc_numeric sum   = gnc_numeric_add(a, b, denom, how);
    gnc_numeric exact = gnc_numeric_add(a, b, GNC_DENOM_AUTO,
                                        GNC_HOW_DENOM_REDUCE);
    gnc_numeric err   = gnc_numeric_sub(sum, exact, GNC_DENOM_AUTO,
                                        GNC_HOW_DENOM_REDUCE);

    if (error)
    {
        *error = err;
    }
    return sum;
}
Пример #4
0
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, *txn_curr = xaccTransGetCurrency (trans);

        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(txn_curr),
                                         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);
            }
        }
    }
    return imbalance;
}
Пример #5
0
/*
 * Given an owner, extract the open balance from the owner and then
 * convert it to the desired currency.
 */
gnc_numeric
gncOwnerGetBalanceInCurrency (const GncOwner *owner,
                              const gnc_commodity *report_currency)
{
    gnc_numeric balance = gnc_numeric_zero ();
    GList *acct_list, *acct_node, *acct_types, *lot_list = NULL, *lot_node;
    QofBook *book;
    gnc_commodity *owner_currency;
    GNCPriceDB *pdb;

    g_return_val_if_fail (owner, gnc_numeric_zero ());

    /* Get account list */
    book       = qof_instance_get_book (qofOwnerGetOwner (owner));
    acct_list  = gnc_account_get_descendants (gnc_book_get_root_account (book));
    acct_types = gncOwnerGetAccountTypesList (owner);
    owner_currency = gncOwnerGetCurrency (owner);

    /* For each account */
    for (acct_node = acct_list; acct_node; acct_node = acct_node->next)
    {
        Account *account = acct_node->data;

        /* Check if this account can have lots for the owner, otherwise skip to next */
        if (g_list_index (acct_types, (gpointer)xaccAccountGetType (account))
                == -1)
            continue;


        if (!gnc_commodity_equal (owner_currency, xaccAccountGetCommodity (account)))
            continue;

        /* Get a list of open lots for this owner and account */
        lot_list = xaccAccountFindOpenLots (account, gncOwnerLotMatchOwnerFunc,
                                            (gpointer)owner, NULL);
        /* For each lot */
        for (lot_node = lot_list; lot_node; lot_node = lot_node->next)
        {
            GNCLot *lot = lot_node->data;
            gnc_numeric lot_balance = gnc_lot_get_balance (lot);
            GncInvoice *invoice = gncInvoiceGetInvoiceFromLot(lot);
            if (invoice)
               balance = gnc_numeric_add (balance, lot_balance,
                                          gnc_commodity_get_fraction (owner_currency), GNC_HOW_RND_ROUND_HALF_UP);
        }
    }

    pdb = gnc_pricedb_get_db (book);

    if (report_currency)
        balance = gnc_pricedb_convert_balance_latest_price (
                      pdb, balance, owner_currency, report_currency);

    return balance;
}
Пример #6
0
/* return the total for this list */
gnc_numeric gncAccountValueTotal (GList *list)
{
    gnc_numeric total = gnc_numeric_zero ();

    for ( ; list ; list = list->next)
    {
        GncAccountValue *val = list->data;
        total = gnc_numeric_add (total, val->value, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
    }
    return total;
}
Пример #7
0
gnc_numeric
gnc_numeric_sub(gnc_numeric a, gnc_numeric b,
                gint64 denom, gint how)
{
    gnc_numeric nb;
    if (gnc_numeric_check(a) || gnc_numeric_check(b))
    {
        return gnc_numeric_error(GNC_ERROR_ARG);
    }

    nb = b;
    nb.num = -nb.num;
    return gnc_numeric_add (a, nb, denom, how);
}
Пример #8
0
// Note this is a recursive function. It presumes the number of splits
// in avail_splits is relatively low. With many splits the performance will
// quickly degrade.
// Careful: this function assumes all splits in avail_splits to be valid
// and with values of opposite sign of target_value
// Ignoring this can cause unexpected results!
static SplitList *
gncSLFindOffsSplits (SplitList *avail_splits, gnc_numeric target_value)
{
    gint curr_recurse_level = 0;
    gint max_recurse_level = g_list_length (avail_splits) - 1;

    if (!avail_splits)
        return NULL;

    for (curr_recurse_level = 0;
            curr_recurse_level <= max_recurse_level;
            curr_recurse_level++)
    {
        SplitList *split_iter = NULL;
        for (split_iter = avail_splits; split_iter; split_iter = split_iter->next)
        {
            Split *split = split_iter->data;
            SplitList *match_splits = NULL;
            gnc_numeric split_value, remaining_value;

            split_value = xaccSplitGetValue (split);
            // Attention: target_value and split_value are of opposite sign
            // So to get the remaining target value, they should be *added*
            remaining_value = gnc_numeric_add (target_value, split_value,
                                               GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);

            if (curr_recurse_level == 0)
            {
                if (gnc_numeric_zero_p (remaining_value))
                    match_splits = g_list_prepend (NULL, split);
            }
            else
            {
                if (gnc_numeric_positive_p (target_value) ==
                        gnc_numeric_positive_p (remaining_value))
                    match_splits = gncSLFindOffsSplits (split_iter->next,
                                                        remaining_value);
            }

            if (match_splits)
                return g_list_prepend (match_splits, split);
        }
    }

    return NULL;
}
Пример #9
0
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;
}
Пример #10
0
static void *
numeric_ops(char op_sym,
            void *left_value,
            void *right_value)
{
    ParserNum *left = left_value;
    ParserNum *right = right_value;
    ParserNum *result;

    if ((left == NULL) || (right == NULL))
        return NULL;

    result = (op_sym == ASN_OP) ? left : g_new0(ParserNum, 1);

    switch (op_sym)
    {
    case ADD_OP:
        result->value = gnc_numeric_add (left->value, right->value,
                                         GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT);
        break;
    case SUB_OP:
        result->value = gnc_numeric_sub (left->value, right->value,
                                         GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT);
        break;
    case DIV_OP:
        result->value = gnc_numeric_div (left->value, right->value,
                                         GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT);
        break;
    case MUL_OP:
        result->value = gnc_numeric_mul (left->value, right->value,
                                         GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT);
        break;
    case ASN_OP:
        result->value = right->value;
        break;
    }

    return result;
}
Пример #11
0
static guint
sxftd_add_template_trans(SXFromTransInfo *sxfti)
{

    Transaction *tr = sxfti->trans;
    GList *tt_list = NULL;
    GList *splits, *template_splits = NULL;
    TTInfo *tti = gnc_ttinfo_malloc();
    TTSplitInfo *ttsi;
    Split *sp;
    gnc_numeric runningBalance;
    gnc_numeric split_value;
    const char *tmpStr;

    runningBalance = gnc_numeric_zero();

    gnc_ttinfo_set_description(tti, xaccTransGetDescription(tr));
    gnc_ttinfo_set_num(tti, gnc_get_num_action(tr, NULL));
    gnc_ttinfo_set_currency(tti, xaccTransGetCurrency(tr));

    for (splits = xaccTransGetSplitList(tr); splits; splits = splits->next)
    {
        sp = splits->data;
        ttsi = gnc_ttsplitinfo_malloc();
        gnc_ttsplitinfo_set_action(ttsi, gnc_get_num_action(NULL, sp));
        split_value = xaccSplitGetValue(sp);
        gnc_ttsplitinfo_set_memo(ttsi, xaccSplitGetMemo(sp));

        runningBalance = gnc_numeric_add( runningBalance, split_value,
                                          100, (GNC_DENOM_AUTO | GNC_HOW_DENOM_LCD) );

        if (gnc_numeric_positive_p(split_value))
        {
            tmpStr = xaccPrintAmount( split_value,
                                      gnc_default_print_info(FALSE) );
            gnc_ttsplitinfo_set_debit_formula( ttsi, tmpStr );
        }
        else
        {
            /* Negate the numeric so it prints w/o the sign at the front. */
            tmpStr = xaccPrintAmount( gnc_numeric_neg( split_value ),
                                      gnc_default_print_info(FALSE) );
            gnc_ttsplitinfo_set_credit_formula( ttsi, tmpStr );
        }

        /* Copy over per-split account info */
        gnc_ttsplitinfo_set_account( ttsi, xaccSplitGetAccount( sp ) );

        template_splits = g_list_append(template_splits, ttsi);
    }

    if ( ! gnc_numeric_zero_p( runningBalance )
            && !gnc_verify_dialog( (GtkWidget *)sxfti->dialog,
                                   FALSE, "%s",
                                   _("The Scheduled Transaction Editor "
                                     "cannot automatically balance "
                                     "this transaction. "
                                     "Should it still be "
                                     "entered?") ) )
    {
        return SXFTD_ERRNO_UNBALANCED_XACTION;
    }

    gnc_ttinfo_set_template_splits(tti, template_splits);

    tt_list = g_list_append(tt_list, tti);

    gnc_suspend_gui_refresh ();
    xaccSchedXactionSetTemplateTrans(sxfti->sx, tt_list,
                                     gnc_get_current_book ());
    gnc_resume_gui_refresh ();

    return 0;
}
Пример #12
0
void gncOwnerAutoApplyPaymentsWithLots (const GncOwner *owner, GList *lots)
{
    GList *base_iter;

    /* General note: in the code below the term "payment" can
     * both mean a true payment or a document of
     * the opposite sign (invoice vs credit note) relative to
     * the lot being processed. In general this function will
     * perform a balancing action on a set of lots, so you
     * will also find frequent references to balancing instead. */

    /* Payments can only be applied when at least an owner
     * and a list of lots to use are given */
    if (!owner) return;
    if (!lots) return;

    for (base_iter = lots; base_iter; base_iter = base_iter->next)
    {
        GNCLot *base_lot = base_iter->data;
        QofBook *book;
        Account *acct;
        const gchar *name;
        GList *lot_list, *lot_iter;
        Transaction *txn = NULL;
        gnc_numeric base_lot_bal, val_to_pay, val_paid = { 0, 1 };
        gboolean base_bal_is_pos;
        const gchar *action, *memo;

        /* Only attempt to apply payments to open lots.
         * Note that due to the iterative nature of this function lots
         * in the list may become closed before they are evaluated as
         * base lot, so we should check this for each lot. */
        base_lot_bal = gnc_lot_get_balance (base_lot);
        if (gnc_numeric_zero_p (base_lot_bal))
            continue;

        book = gnc_lot_get_book (base_lot);
        acct = gnc_lot_get_account (base_lot);
        name = gncOwnerGetName (gncOwnerGetEndOwner (owner));
        lot_list = base_iter->next;

        /* Strings used when creating splits later on. */
        action = _("Lot Link");
        memo   = _("Internal link between invoice and payment lots");

        /* Note: to balance the lot the payment to assign
         * must have the opposite sign of the existing lot balance */
        val_to_pay = gnc_numeric_neg (base_lot_bal);
        base_bal_is_pos = gnc_numeric_positive_p (base_lot_bal);


        /* Create splits in a linking transaction between lots until
         * - either the invoice lot is balanced
         * - or there are no more balancing lots.
         */
        for (lot_iter = lot_list; lot_iter; lot_iter = lot_iter->next)
        {
            gnc_numeric payment_lot_balance;
            Split *split;
            Account *bal_acct;
            gnc_numeric  split_amt;

            GNCLot *balancing_lot = lot_iter->data;

            /* Only attempt to use open lots to balance the base lot.
             * Note that due to the iterative nature of this function lots
             * in the list may become closed before they are evaluated as
             * base lot, so we should check this for each lot. */
            if (gnc_lot_is_closed (balancing_lot))
                continue;

            /* Balancing transactions for invoice/payments can only happen
             * in the same account. */
            bal_acct = gnc_lot_get_account (balancing_lot);
            if (acct != bal_acct)
                continue;

            payment_lot_balance = gnc_lot_get_balance (balancing_lot);

            /* Only attempt to balance if the base lot and balancing lot are
             * of the opposite sign. (Otherwise we would increase the balance
             * of the lot - Duh */
            if (base_bal_is_pos == gnc_numeric_positive_p (payment_lot_balance))
                continue;

            /*
             * If there is less to pay than there's open in the lot; we're done -- apply the base_lot_vale.
             * Note that payment_value and balance are opposite in sign, so we have to compare absolute values here
             *
             * Otherwise, apply the balance, subtract that from the payment_value,
             * and move on to the next one.
             */
            if (gnc_numeric_compare (gnc_numeric_abs (val_to_pay), gnc_numeric_abs (payment_lot_balance)) <= 0)
            {
                /* abs(val_to_pay) <= abs(balance) */
                split_amt = val_to_pay;
            }
            else
            {
                /* abs(val_to_pay) > abs(balance)
                 * Remember payment_value and balance are opposite in sign,
                 * and we want a payment to neutralize the current balance
                 * so we need to negate here */
                split_amt = payment_lot_balance;
            }

            /* If not created yet, create a new transaction linking
             * the base lot and the balancing lot(s) */
            if (!txn)
            {
                Timespec ts = xaccTransRetDatePostedTS (xaccSplitGetParent (gnc_lot_get_latest_split (base_lot)));

                xaccAccountBeginEdit (acct);

                txn = xaccMallocTransaction (book);
                xaccTransBeginEdit (txn);

                xaccTransSetDescription (txn, name ? name : "");
                xaccTransSetCurrency (txn, xaccAccountGetCommodity(acct));
                xaccTransSetDateEnteredSecs (txn, gnc_time (NULL));
                xaccTransSetDatePostedTS (txn, &ts);
                xaccTransSetTxnType (txn, TXN_TYPE_LINK);
            }

            /* Create the split for this link in current balancing lot */
            split = xaccMallocSplit (book);
            xaccSplitSetMemo (split, memo);
            /* set Action using utility function */
            gnc_set_num_action (NULL, split, NULL, action);
            xaccAccountInsertSplit (acct, split);
            xaccTransAppendSplit (txn, split);
            xaccSplitSetBaseValue (split, gnc_numeric_neg (split_amt), xaccAccountGetCommodity(acct));
            gnc_lot_add_split (balancing_lot, split);

            /* If the balancing lot was linked to a document (invoice/credit note),
             * send an event for it as well so it gets potentially updated as paid */
            {
                GncInvoice *this_invoice = gncInvoiceGetInvoiceFromLot(balancing_lot);
                if (this_invoice)
                    qof_event_gen (QOF_INSTANCE(this_invoice), QOF_EVENT_MODIFY, NULL);
            }

            val_paid   = gnc_numeric_add (val_paid, split_amt, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
            val_to_pay = gnc_numeric_sub (val_to_pay, split_amt, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
            if (gnc_numeric_zero_p (val_to_pay))
                break;
        }


        /* If the above loop managed to create a transaction and some balancing splits,
         * create the final split for the link transaction in the base lot */
        if (txn)
        {
            GncInvoice *this_invoice;
            Split *split = xaccMallocSplit (book);

            xaccSplitSetMemo (split, memo);
            /* set Action with utiltity function */
            gnc_set_num_action (NULL, split, NULL, action);
            xaccAccountInsertSplit (acct, split);
            xaccTransAppendSplit (txn, split);
            xaccSplitSetBaseValue (split, val_paid, xaccAccountGetCommodity(acct));
            gnc_lot_add_split (base_lot, split);

            xaccTransCommitEdit (txn);
            xaccAccountCommitEdit (acct);

            /* If the base lot was linked to a document (invoice/credit note),
             * send an event for it as well so it gets potentially updated as paid */
            this_invoice = gncInvoiceGetInvoiceFromLot(base_lot);
            if (this_invoice)
                qof_event_gen (QOF_INSTANCE(this_invoice), QOF_EVENT_MODIFY, NULL);

        }

    }
}
Пример #13
0
/** Creates a list of transactions from parsed data. Transactions that
 * could be created from rows are placed in parse_data->transactions;
 * rows that fail are placed in parse_data->error_lines. (Note: there
 * is no way for this function to "fail," i.e. it only returns 0, so
 * it may be changed to a void function in the future.)
 * @param parse_data Data that is being parsed
 * @param account Account with which transactions are created
 * @param redo_errors TRUE to convert only error data, FALSE for all data
 * @return 0 on success, 1 on failure
 */
int gnc_csv_parse_to_trans (GncCsvParseData* parse_data, Account* account,
                           gboolean redo_errors)
{
    gboolean hasBalanceColumn;
    int i, j, max_cols = 0;
    GArray* column_types = parse_data->column_types;
    GList *error_lines = NULL, *begin_error_lines = NULL;

    /* last_transaction points to the last element in
     * parse_data->transactions, or NULL if it's empty. */
    GList* last_transaction = NULL;

    /* Free parse_data->error_lines and parse_data->transactions if they
     * already exist. */
    if (redo_errors) /* If we're redoing errors, we save freeing until the end. */
    {
        begin_error_lines = error_lines = parse_data->error_lines;
    }
    else
    {
        if (parse_data->error_lines != NULL)
        {
            g_list_free(parse_data->error_lines);
        }
        if (parse_data->transactions != NULL)
        {
            g_list_free (parse_data->transactions);
        }
    }
    parse_data->error_lines = NULL;

    if (redo_errors) /* If we're looking only at error data ... */
    {
        if (parse_data->transactions == NULL)
        {
            last_transaction = NULL;
        }
        else
        {
            /* Move last_transaction to the end. */
            last_transaction = parse_data->transactions;
            while (g_list_next (last_transaction) != NULL)
            {
                last_transaction = g_list_next (last_transaction);
            }
        }
        /* ... we use only the lines in error_lines. */
        if (error_lines == NULL)
            i = parse_data->orig_lines->len; /* Don't go into the for loop. */
        else
            i = GPOINTER_TO_INT(error_lines->data);
    }
    else /* Otherwise, we look at all the data. */
    {
        /* The following while-loop effectively behaves like the following for-loop:
         * for(i = 0; i < parse_data->orig_lines->len; i++). */
        i = parse_data->start_row;
        last_transaction = NULL;
    }

    /* set parse_data->end_row to number of lines */
    if (parse_data->end_row > parse_data->orig_lines->len)
        parse_data->end_row = parse_data->orig_lines->len;

    while (i < parse_data->end_row)
    {
        GPtrArray* line = parse_data->orig_lines->pdata[i];
        /* This flag is TRUE if there are any errors in this row. */
        gboolean errors = FALSE;
        gchar* error_message = NULL;
        TransPropertyList* list = trans_property_list_new (account, parse_data->date_format, parse_data->currency_format);
        GncCsvTransLine* trans_line = NULL;

        for (j = 0; j < line->len; j++)
        {
            /* We do nothing in "None" or "Account" columns. */
            if ((column_types->data[j] != GNC_CSV_NONE) && (column_types->data[j] != GNC_CSV_ACCOUNT))
            {
                /* Affect the transaction appropriately. */
                TransProperty* property = trans_property_new (column_types->data[j], list);
                gboolean succeeded = trans_property_set (property, line->pdata[j]);

                /* TODO Maybe move error handling to within TransPropertyList functions? */
                if (succeeded)
                {
                    trans_property_list_add (property);
                }
                else
                {
                    errors = TRUE;
                    error_message = g_strdup_printf (_("%s column could not be understood."),
                                                    _(gnc_csv_column_type_strs[property->type]));
                    trans_property_free (property);
                    break;
                }
            }
        }

        /* If we had success, add the transaction to parse_data->transaction. */
        if (!errors)
        {
            trans_line = trans_property_list_to_trans (list, &error_message);
            errors = trans_line == NULL;
        }

        trans_property_list_free (list);

        /* If there were errors, add this line to parse_data->error_lines. */
        if (errors)
        {
            parse_data->error_lines = g_list_append (parse_data->error_lines,
                                                    GINT_TO_POINTER(i));
            /* If there's already an error message, we need to replace it. */
            if (line->len > (int)(parse_data->orig_row_lengths->data[i]))
            {
                g_free(line->pdata[line->len - 1]);
                line->pdata[line->len - 1] = error_message;
            }
            else
            {
                /* Put the error message at the end of the line. */
                g_ptr_array_add (line, error_message);
            }
        }
        else
        {
            /* If all went well, add this transaction to the list. */
            trans_line->line_no = i;

            /* We keep the transactions sorted by date. We start at the end
             * of the list and go backward, simply because the file itself
             * is probably also sorted by date (but we need to handle the
             * exception anyway). */

            /* If we can just put it at the end, do so and increment last_transaction. */
            if (last_transaction == NULL ||
                    xaccTransGetDate (((GncCsvTransLine*)(last_transaction->data))->trans) <= xaccTransGetDate (trans_line->trans))
            {
                parse_data->transactions = g_list_append (parse_data->transactions, trans_line);
                /* If this is the first transaction, we need to get last_transaction on track. */
                if (last_transaction == NULL)
                    last_transaction = parse_data->transactions;
                else /* Otherwise, we can just continue. */
                    last_transaction = g_list_next (last_transaction);
            }
            /* Otherwise, search backward for the correct spot. */
            else
            {
                GList* insertion_spot = last_transaction;
                while (insertion_spot != NULL &&
                        xaccTransGetDate (((GncCsvTransLine*)(insertion_spot->data))->trans) > xaccTransGetDate (trans_line->trans))
                {
                    insertion_spot = g_list_previous (insertion_spot);
                }
                /* Move insertion_spot one location forward since we have to
                 * use the g_list_insert_before function. */
                if (insertion_spot == NULL) /* We need to handle the case of inserting at the beginning of the list. */
                    insertion_spot = parse_data->transactions;
                else
                    insertion_spot = g_list_next (insertion_spot);

                parse_data->transactions = g_list_insert_before (parse_data->transactions, insertion_spot, trans_line);
            }
        }

        /* Increment to the next row. */
        if (redo_errors)
        {
            /* Move to the next error line in the list. */
            error_lines = g_list_next (error_lines);
            if (error_lines == NULL)
                i = parse_data->orig_lines->len; /* Don't continue the for loop. */
            else
                i = GPOINTER_TO_INT(error_lines->data);
        }
        else
        {
            if (parse_data->skip_rows == FALSE)
                i++;
            else
                i = i + 2;
        }
    }

    /* If we have a balance column, set the appropriate amounts on the transactions. */
    hasBalanceColumn = FALSE;
    for (i = 0; i < parse_data->column_types->len; i++)
    {
        if (parse_data->column_types->data[i] == GNC_CSV_BALANCE)
        {
            hasBalanceColumn = TRUE;
            break;
        }
    }

    if (hasBalanceColumn)
    {
        GList* transactions = parse_data->transactions;

        /* balance_offset is how much the balance currently in the account
         * differs from what it will be after the transactions are
         * imported. This will be sum of all the previous transactions for
         * any given transaction. */
        gnc_numeric balance_offset = double_to_gnc_numeric (0.0,
                                     xaccAccountGetCommoditySCU (account),
                                     GNC_HOW_RND_ROUND_HALF_UP);
        while (transactions != NULL)
        {
            GncCsvTransLine* trans_line = (GncCsvTransLine*)transactions->data;
            if (trans_line->balance_set)
            {
                time64 date = xaccTransGetDate (trans_line->trans);
                /* Find what the balance should be by adding the offset to the actual balance. */
                gnc_numeric existing_balance = gnc_numeric_add (balance_offset,
                                               xaccAccountGetBalanceAsOfDate (account, date),
                                               xaccAccountGetCommoditySCU (account),
                                               GNC_HOW_RND_ROUND_HALF_UP);

                /* The amount of the transaction is the difference between the new and existing balance. */
                gnc_numeric amount = gnc_numeric_sub (trans_line->balance,
                                                     existing_balance,
                                                     xaccAccountGetCommoditySCU (account),
                                                     GNC_HOW_RND_ROUND_HALF_UP);

                SplitList* splits = xaccTransGetSplitList (trans_line->trans);
                while (splits)
                {
                    SplitList* next_splits = g_list_next (splits);
                    xaccSplitDestroy ((Split*)splits->data);
                    splits = next_splits;
                }

                trans_add_split (trans_line->trans, account,
                                gnc_account_get_book (account), amount, trans_line->num);
                if (trans_line->num)
                    g_free (trans_line->num);

                /* This new transaction needs to be added to the balance offset. */
                balance_offset = gnc_numeric_add (balance_offset,
                                                 amount,
                                                 xaccAccountGetCommoditySCU (account),
                                                 GNC_HOW_RND_ROUND_HALF_UP);
            }
            transactions = g_list_next (transactions);
        }
    }

    if (redo_errors) /* Now that we're at the end, we do the freeing. */
    {
        g_list_free (begin_error_lines);
    }

    /* We need to resize parse_data->column_types since errors may have added columns. */
    for (i = 0; i < parse_data->orig_lines->len; i++)
    {
        if (max_cols < ((GPtrArray*)(parse_data->orig_lines->pdata[i]))->len)
            max_cols = ((GPtrArray*)(parse_data->orig_lines->pdata[i]))->len;
    }
    i = parse_data->column_types->len;
    parse_data->column_types = g_array_set_size (parse_data->column_types, max_cols);
    for (; i < max_cols; i++)
    {
        parse_data->column_types->data[i] = GNC_CSV_NONE;
    }

    return 0;
}
Пример #14
0
/** Create a Transaction from a TransPropertyList.
 * @param list The list of properties
 * @param error Contains an error on failure
 * @return On success, a GncCsvTransLine; on failure, the trans pointer is NULL
 */
static GncCsvTransLine* trans_property_list_to_trans (TransPropertyList* list, gchar** error)
{
    GncCsvTransLine* trans_line = g_new (GncCsvTransLine, 1);
    GList* properties_begin = list->properties;
    QofBook* book = gnc_account_get_book (list->account);
    gnc_commodity* currency = xaccAccountGetCommodity (list->account);
    gnc_numeric amount = double_to_gnc_numeric (0.0, xaccAccountGetCommoditySCU (list->account),
                         GNC_HOW_RND_ROUND_HALF_UP);
    gchar *num = NULL;

    /* This flag is set to TRUE if we can use the "Deposit" or "Withdrawal" column. */
    gboolean amount_set = FALSE;

    /* The balance is 0 by default. */
    trans_line->balance_set = FALSE;
    trans_line->balance = amount;
    trans_line->num = NULL;

    /* We make the line_no -1 just to mark that it hasn't been set. We
     * may get rid of line_no soon anyway, so it's not particularly
     * important. */
    trans_line->line_no = -1;

    /* Make sure this is a transaction with all the columns we need. */
    if (!trans_property_list_verify_essentials (list, error))
    {
        g_free(trans_line);
        return NULL;
    }

    trans_line->trans = xaccMallocTransaction (book);
    xaccTransBeginEdit (trans_line->trans);
    xaccTransSetCurrency (trans_line->trans, currency);

    /* Go through each of the properties and edit the transaction accordingly. */
    list->properties = properties_begin;
    while (list->properties != NULL)
    {
        TransProperty* prop = (TransProperty*)(list->properties->data);
        switch (prop->type)
        {
        case GNC_CSV_DATE:
            xaccTransSetDatePostedSecsNormalized (trans_line->trans, *((time64*)(prop->value)));
            break;

        case GNC_CSV_DESCRIPTION:
            xaccTransSetDescription (trans_line->trans, (char*)(prop->value));
            break;

        case GNC_CSV_NOTES:
            xaccTransSetNotes (trans_line->trans, (char*)(prop->value));
            break;

        case GNC_CSV_NUM:
            /* the 'num' is saved and passed to 'trans_add_split' below where
             * 'gnc_set_num_action' is used to set tran-num and/or split-action
             * per book option */
            num = g_strdup ((char*)(prop->value));
            /* the 'num' is also saved and used in 'gnc_csv_parse_to_trans' when
             * it calls 'trans_add_split' after deleting the splits added below
             * when a balance is used by the user */
            trans_line->num = g_strdup ((char*)(prop->value));
            break;

        case GNC_CSV_DEPOSIT: /* Add deposits to the existing amount. */
            if (prop->value != NULL)
            {
                amount = gnc_numeric_add (*((gnc_numeric*)(prop->value)),
                                         amount,
                                         xaccAccountGetCommoditySCU (list->account),
                                         GNC_HOW_RND_ROUND_HALF_UP);
                amount_set = TRUE;
                /* We will use the "Deposit" and "Withdrawal" columns in preference to "Balance". */
                trans_line->balance_set = FALSE;
            }
            break;

        case GNC_CSV_WITHDRAWAL: /* Withdrawals are just negative deposits. */
            if (prop->value != NULL)
            {
                amount = gnc_numeric_add (gnc_numeric_neg(*((gnc_numeric*)(prop->value))),
                                         amount,
                                         xaccAccountGetCommoditySCU (list->account),
                                         GNC_HOW_RND_ROUND_HALF_UP);
                amount_set = TRUE;
                /* We will use the "Deposit" and "Withdrawal" columns in preference to "Balance". */
                trans_line->balance_set = FALSE;
            }
            break;

        case GNC_CSV_BALANCE: /* The balance gets stored in a separate field in trans_line. */
            /* We will use the "Deposit" and "Withdrawal" columns in preference to "Balance". */
            if (!amount_set && prop->value != NULL)
            {
                /* This gets put into the actual transaction at the end of gnc_csv_parse_to_trans. */
                trans_line->balance = *((gnc_numeric*)(prop->value));
                trans_line->balance_set = TRUE;
            }
            break;
        }
        list->properties = g_list_next (list->properties);
    }

    /* Add a split with the cumulative amount value. */
    trans_add_split (trans_line->trans, list->account, book, amount, num);
    if (num)
        g_free (num);

    return trans_line;
}
Пример #15
0
/*@ null @*/ GSList*
gnc_sql_get_account_balances_slist( GncSqlBackend* be )
{
#if LOAD_TRANSACTIONS_AS_NEEDED
    GncSqlResult* result;
    GncSqlStatement* stmt;
    gchar* buf;
    GSList* bal_slist = NULL;

    g_return_val_if_fail( be != NULL, NULL );

    buf = g_strdup_printf( "SELECT account_guid, reconcile_state, sum(quantity_num) as quantity_num, quantity_denom FROM %s GROUP BY account_guid, reconcile_state, quantity_denom ORDER BY account_guid, reconcile_state",
                           SPLIT_TABLE );
    stmt = gnc_sql_create_statement_from_sql( be, buf );
    g_assert( stmt != NULL );
    g_free( buf );
    result = gnc_sql_execute_select_statement( be, stmt );
    gnc_sql_statement_dispose( stmt );
    if ( result != NULL )
    {
        GncSqlRow* row;
        acct_balances_t* bal = NULL;

        row = gnc_sql_result_get_first_row( result );
        while ( row != NULL )
        {
            single_acct_balance_t* single_bal;

            // Get the next reconcile state balance and merge with other balances
            single_bal = load_single_acct_balances( be, row );
            if ( single_bal != NULL )
            {
                if ( bal != NULL && bal->acct != single_bal->acct )
                {
                    bal->cleared_balance = gnc_numeric_add( bal->cleared_balance, bal->reconciled_balance,
                                                            GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD );
                    bal->balance = gnc_numeric_add( bal->balance, bal->cleared_balance,
                                                    GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD );
                    bal_slist = g_slist_append( bal_slist, bal );
                    bal = NULL;
                }
                if ( bal == NULL )
                {
                    bal = g_malloc( (gsize)sizeof(acct_balances_t) );
                    g_assert( bal != NULL );

                    bal->acct = single_bal->acct;
                    bal->balance = gnc_numeric_zero();
                    bal->cleared_balance = gnc_numeric_zero();
                    bal->reconciled_balance = gnc_numeric_zero();
                }
                if ( single_bal->reconcile_state == 'n' )
                {
                    bal->balance = gnc_numeric_add( bal->balance, single_bal->balance,
                                                    GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD );
                }
                else if ( single_bal->reconcile_state == 'c' )
                {
                    bal->cleared_balance = gnc_numeric_add( bal->cleared_balance, single_bal->balance,
                                                            GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD );
                }
                else if ( single_bal->reconcile_state == 'y' )
                {
                    bal->reconciled_balance = gnc_numeric_add( bal->reconciled_balance, single_bal->balance,
                                              GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD );
                }
                g_free( single_bal );
            }
            row = gnc_sql_result_get_next_row( result );
        }

        // Add the final balance
        if ( bal != NULL )
        {
            bal->cleared_balance = gnc_numeric_add( bal->cleared_balance, bal->reconciled_balance,
                                                    GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD );
            bal->balance = gnc_numeric_add( bal->balance, bal->cleared_balance,
                                            GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD );
            bal_slist = g_slist_append( bal_slist, bal );
        }
        gnc_sql_result_dispose( result );
    }

    return bal_slist;
#else
    return NULL;
#endif
}
Пример #16
0
/**
 * Executes a transaction query statement and loads the transactions and all
 * of the splits.
 *
 * @param be SQL backend
 * @param stmt SQL statement
 */
static void
query_transactions( GncSqlBackend* be, GncSqlStatement* stmt )
{
    GncSqlResult* result;

    g_return_if_fail( be != NULL );
    g_return_if_fail( stmt != NULL );

    result = gnc_sql_execute_select_statement( be, stmt );
    if ( result != NULL )
    {
        GList* tx_list = NULL;
        GList* node;
        GncSqlRow* row;
        Transaction* tx;
#if LOAD_TRANSACTIONS_AS_NEEDED
        GSList* bal_list = NULL;
        GSList* nextbal;
        Account* root = gnc_book_get_root_account( be->book );

        qof_event_suspend();
        xaccAccountBeginEdit( root );

        // Save the start/ending balances (balance, cleared and reconciled) for
        // every account.
        gnc_account_foreach_descendant( gnc_book_get_root_account( be->primary_book ),
                                        save_account_balances,
                                        &bal_list );
#endif

        // Load the transactions
        row = gnc_sql_result_get_first_row( result );
        while ( row != NULL )
        {
            tx = load_single_tx( be, row );
            if ( tx != NULL )
            {
                tx_list = g_list_prepend( tx_list, tx );
            }
            row = gnc_sql_result_get_next_row( result );
        }
        gnc_sql_result_dispose( result );

        // Load all splits and slots for the transactions
        if ( tx_list != NULL )
        {
            gnc_sql_slots_load_for_list( be, tx_list );
            load_splits_for_tx_list( be, tx_list );
        }

        // Commit all of the transactions
        for ( node = tx_list; node != NULL; node = node->next )
        {
            Transaction* pTx = GNC_TRANSACTION(node->data);
            xaccTransCommitEdit( pTx );
        }
        g_list_free( tx_list );

#if LOAD_TRANSACTIONS_AS_NEEDED
        // Update the account balances based on the loaded splits.  If the end
        // balance has changed, update the start balance so that the end
        // balance is the same as it was before the splits were loaded.
        // Repeat for cleared and reconciled balances.
        for ( nextbal = bal_list; nextbal != NULL; nextbal = nextbal->next )
        {
            full_acct_balances_t* balns = (full_acct_balances_t*)nextbal->data;
            gnc_numeric* pnew_end_bal;
            gnc_numeric* pnew_end_c_bal;
            gnc_numeric* pnew_end_r_bal;
            gnc_numeric adj;

            g_object_get( balns->acc,
                          "end-balance", &pnew_end_bal,
                          "end-cleared-balance", &pnew_end_c_bal,
                          "end-reconciled-balance", &pnew_end_r_bal,
                          NULL );

            if ( !gnc_numeric_eq( *pnew_end_bal, balns->end_bal ) )
            {
                adj = gnc_numeric_sub( balns->end_bal, *pnew_end_bal,
                                       GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD );
                balns->start_bal = gnc_numeric_add( balns->start_bal, adj,
                                                    GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD );
                g_object_set( balns->acc, "start-balance", &balns->start_bal, NULL );
            }
            if ( !gnc_numeric_eq( *pnew_end_c_bal, balns->end_cleared_bal ) )
            {
                adj = gnc_numeric_sub( balns->end_cleared_bal, *pnew_end_c_bal,
                                       GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD );
                balns->start_cleared_bal = gnc_numeric_add( balns->start_cleared_bal, adj,
                                           GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD );
                g_object_set( balns->acc, "start-cleared-balance", &balns->start_cleared_bal, NULL );
            }
            if ( !gnc_numeric_eq( *pnew_end_r_bal, balns->end_reconciled_bal ) )
            {
                adj = gnc_numeric_sub( balns->end_reconciled_bal, *pnew_end_r_bal,
                                       GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD );
                balns->start_reconciled_bal = gnc_numeric_add( balns->start_reconciled_bal, adj,
                                              GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD );
                g_object_set( balns->acc, "start-reconciled-balance", &balns->start_reconciled_bal, NULL );
            }
            xaccAccountRecomputeBalance( balns->acc );
            g_free( pnew_end_bal );
            g_free( pnew_end_c_bal );
            g_free( pnew_end_r_bal );
            g_free( balns );
        }
        if ( bal_list != NULL )
        {
            g_slist_free( bal_list );
        }

        xaccAccountCommitEdit( root );
        qof_event_resume();
#endif
    }
}
Пример #17
0
static void
check_add_subtract_overflow (void)
{
    int i;

    for (i = 0; i < NREPS; i++)
    {
	/* Div to avoid addition overflows; we're looking for lcd conversion overflows here. */

	int exp_a = rand () % 1000;
	int exp_b = rand () % 1000;
        gint64 bin_deno_a = (exp_a == 0 ? 1 : exp_a);
	gint64 bin_deno_b = (exp_b == 0 ? 1 : exp_b);
/*
	int exp_a = rand () % 11;
	int exp_b = rand () % 11;
	gint64 bin_deno_a = (1 << exp_a);
	gint64 bin_deno_b = (1 << exp_a);
*/
	gint64 dec_deno_a = powten (exp_a % 7);
	gint64 dec_deno_b = powten (exp_b % 7);
        gint64 na = get_random_gint64 () % (1000000 * dec_deno_a);
        gint64 nb = get_random_gint64 () % (1000000 * dec_deno_b);
	gnc_numeric result;
	GNCNumericErrorCode err;
	gchar *errmsg;

        gnc_numeric ba = gnc_numeric_create(na, bin_deno_a);
        gnc_numeric bb = gnc_numeric_create(nb, bin_deno_b);
        gnc_numeric da = gnc_numeric_create(na, dec_deno_a);
        gnc_numeric db = gnc_numeric_create(nb, dec_deno_b);
	gchar *ba_str = gnc_numeric_to_string (ba);
	gchar *bb_str = gnc_numeric_to_string (bb);
	gchar *da_str = gnc_numeric_to_string (da);
	gchar *db_str = gnc_numeric_to_string (db);


        /* Add */

	result = gnc_numeric_add(ba, bb, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT);
	err = gnc_numeric_check (result);
	errmsg = g_strdup_printf ("%s + %s raised %s", ba_str, bb_str,
				  gnc_numeric_errorCode_to_string (err));
	do_test (err == 0, errmsg);
	g_free (errmsg);

	result = gnc_numeric_add(da, bb, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT);
	err = gnc_numeric_check (result);
	errmsg = g_strdup_printf ("%s + %s raised %s", da_str, bb_str,
				  gnc_numeric_errorCode_to_string (err));
	do_test (err == 0, errmsg);
	g_free (errmsg);
	result = gnc_numeric_add(ba, db, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT);
	err = gnc_numeric_check (result);
	errmsg = g_strdup_printf ("%s + %s raised %s", ba_str, db_str,
				  gnc_numeric_errorCode_to_string (err));
	do_test (err == 0, errmsg);
	g_free (errmsg);

	result = gnc_numeric_add(da, db, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT);
	err = gnc_numeric_check (result);
	errmsg = g_strdup_printf ("%s + %s raised %s", da_str, db_str,
				  gnc_numeric_errorCode_to_string (err));
	do_test (err == 0, errmsg);
	g_free (errmsg);
        /* Subtract */

	result = gnc_numeric_sub(ba, bb, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT);
	err = gnc_numeric_check (result);
	errmsg = g_strdup_printf ("%s + %s raised %s", ba_str, bb_str,
				  gnc_numeric_errorCode_to_string (err));
	do_test (err == 0, errmsg);
	g_free (errmsg);

	result = gnc_numeric_sub(da, bb, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT);
	err = gnc_numeric_check (result);
	errmsg = g_strdup_printf ("%s + %s raised %s", da_str, bb_str,
				  gnc_numeric_errorCode_to_string (err));
	do_test (err == 0, errmsg);
	g_free (errmsg);
	result = gnc_numeric_sub(ba, db, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT);
	err = gnc_numeric_check (result);
	errmsg = g_strdup_printf ("%s + %s raised %s", ba_str, db_str,
				  gnc_numeric_errorCode_to_string (err));
	do_test (err == 0, errmsg);
	g_free (errmsg);

	result = gnc_numeric_sub(da, db, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT);
	err = gnc_numeric_check (result);
	errmsg = g_strdup_printf ("%s + %s raised %s", da_str, db_str,
				  gnc_numeric_errorCode_to_string (err));
	do_test (err == 0, errmsg);
	g_free (errmsg);

	g_free (ba_str);
	g_free (bb_str);
	g_free (da_str);
	g_free (db_str);
    }

}
Пример #18
0
/*
 * This is the logic of computing the total for an Entry, so you know
 * what values to put into various Splits or to display in the ledger.
 * In other words, we combine the quantity, unit-price, discount and
 * taxes together, depending on various flags.
 *
 * There are four potental ways to combine these numbers:
 * Discount:     Pre-Tax   Post-Tax
 *   Tax   :     Included  Not-Included
 *
 * The process is relatively simple:
 *
 *  1) compute the agregate price (price*qty)
 *  2) if taxincluded, then back-compute the agregate pre-tax price
 *  3) apply discount and taxes in the appropriate order
 *  4) return the requested results.
 *
 * step 2 can be done with agregate taxes; no need to compute them all
 * unless the caller asked for the tax_value.
 *
 * Note that the returned "value" is such that value + tax == "total
 * to pay," which means in the case of tax-included that the returned
 * "value" may be less than the agregate price, even without a
 * discount.  If you want to display the tax-included value, you need
 * to add the value and taxes together.  In other words, the value is
 * the amount the merchant gets; the taxes are the amount the gov't
 * gets, and the customer pays the sum or value + taxes.
 *
 * The SCU is the denominator to convert the value.
 *
 * The discount return value is just for entertainment -- you may want
 * to let a consumer know how much they saved.
 */
void gncEntryComputeValue (gnc_numeric qty, gnc_numeric price,
                           const GncTaxTable *tax_table, gboolean tax_included,
                           gnc_numeric discount, GncAmountType discount_type,
                           GncDiscountHow discount_how, int SCU,
                           gnc_numeric *value, gnc_numeric *discount_value,
                           GList **tax_value)
{
    gnc_numeric	aggregate;
    gnc_numeric	pretax;
    gnc_numeric	result;
    gnc_numeric	tax;
    gnc_numeric	percent = gnc_numeric_create (100, 1);
    gnc_numeric	tpercent = gnc_numeric_zero ();
    gnc_numeric	tvalue = gnc_numeric_zero ();

    GList * 	entries = gncTaxTableGetEntries (tax_table);
    GList * 	node;

    /* Step 1: compute the aggregate price */

    aggregate = gnc_numeric_mul (qty, price, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);

    /* Step 2: compute the pre-tax aggregate */

    /* First, compute the aggregate tpercent and tvalue numbers */
    for (node = entries; node; node = node->next)
    {
        GncTaxTableEntry *entry = node->data;
        gnc_numeric amount = gncTaxTableEntryGetAmount (entry);

        switch (gncTaxTableEntryGetType (entry))
        {
        case GNC_AMT_TYPE_VALUE:
            tvalue = gnc_numeric_add (tvalue, amount, GNC_DENOM_AUTO,
                                      GNC_HOW_DENOM_LCD);
            break;
        case GNC_AMT_TYPE_PERCENT:
            tpercent = gnc_numeric_add (tpercent, amount, GNC_DENOM_AUTO,
                                        GNC_HOW_DENOM_LCD);
            break;
        default:
            g_warning ("Unknown tax type: %d", gncTaxTableEntryGetType (entry));
        }
    }
    /* now we need to convert from 5% -> .05 */
    tpercent = gnc_numeric_div (tpercent, percent, GNC_DENOM_AUTO,
                                GNC_HOW_DENOM_LCD);

    /* Next, actually compute the pre-tax aggregate value based on the
     * taxincluded flag.
     */
    if (tax_table && tax_included)
    {
        /* Back-compute the pre-tax aggregate value.
         * We know that aggregate = pretax + pretax*tpercent + tvalue, so
         * pretax = (aggregate-tvalue)/(1+tpercent)
         */
        pretax = gnc_numeric_sub (aggregate, tvalue, GNC_DENOM_AUTO,
                                  GNC_HOW_DENOM_LCD);
        pretax = gnc_numeric_div (pretax,
                                  gnc_numeric_add (tpercent,
                                          gnc_numeric_create (1, 1),
                                          GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD),
                                  GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
    }
    else
    {
        pretax = aggregate;
    }

    /* Step 3:  apply discount and taxes in the appropriate order */

    /*
     * There are two ways to apply discounts and taxes.  In one way, you
     * always compute the discount off the pretax number, and compute
     * the taxes off of either the pretax value or "pretax-discount"
     * value.  In the other way, you always compute the tax on "pretax",
     * and compute the discount on either "pretax" or "pretax+taxes".
     *
     * I don't know which is the "correct" way.
     */

    /*
     * Type:	discount	tax
     * PRETAX	pretax		pretax-discount
     * SAMETIME	pretax		pretax
     * POSTTAX	pretax+tax	pretax
     */

    switch (discount_how)
    {
    case GNC_DISC_PRETAX:
    case GNC_DISC_SAMETIME:
        /* compute the discount from pretax */

        if (discount_type == GNC_AMT_TYPE_PERCENT)
        {
            discount = gnc_numeric_div (discount, percent, GNC_DENOM_AUTO,
                                        GNC_HOW_DENOM_LCD);
            discount = gnc_numeric_mul (pretax, discount, GNC_DENOM_AUTO,
                                        GNC_HOW_DENOM_LCD);
        }

        result = gnc_numeric_sub (pretax, discount, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);

        /* Figure out when to apply the tax, pretax or pretax-discount */
        if (discount_how == GNC_DISC_PRETAX)
            pretax = result;
        break;

    case GNC_DISC_POSTTAX:
        /* compute discount on pretax+taxes */

        if (discount_type == GNC_AMT_TYPE_PERCENT)
        {
            gnc_numeric after_tax;

            tax = gnc_numeric_mul (pretax, tpercent, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
            after_tax = gnc_numeric_add (pretax, tax, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
            after_tax = gnc_numeric_add (after_tax, tvalue, GNC_DENOM_AUTO,
                                         GNC_HOW_DENOM_LCD);
            discount = gnc_numeric_div (discount, percent, GNC_DENOM_AUTO,
                                        GNC_HOW_DENOM_LCD);
            discount = gnc_numeric_mul (after_tax, discount, GNC_DENOM_AUTO,
                                        GNC_HOW_DENOM_LCD);
        }

        result = gnc_numeric_sub (pretax, discount, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
        break;

    default:
        g_warning ("unknown DiscountHow value: %d", discount_how);
    }

    /* Step 4:  return the requested results. */

    /* result == amount merchant gets
     * discount == amount of discount
     * need to compute taxes (based on 'pretax') if the caller wants it.
     */

    if (discount_value != NULL)
    {
        if (SCU) discount = gnc_numeric_convert(discount, SCU, GNC_HOW_RND_ROUND);
        *discount_value = discount;
    }

    if (value != NULL)
    {
        if (SCU) result = gnc_numeric_convert(result, SCU, GNC_HOW_RND_ROUND);
        *value = result;
    }

    /* Now... Compute the list of tax values (if the caller wants it) */

    if (tax_value != NULL)
    {
        GList *	taxes = NULL;

        for (node = entries; node; node = node->next)
        {
            GncTaxTableEntry *entry = node->data;
            Account *acc = gncTaxTableEntryGetAccount (entry);
            gnc_numeric amount = gncTaxTableEntryGetAmount (entry);

            g_return_if_fail (acc);

            switch (gncTaxTableEntryGetType (entry))
            {
            case GNC_AMT_TYPE_VALUE:
                if (SCU) amount = gnc_numeric_convert(amount, SCU, GNC_HOW_RND_ROUND);
                taxes = gncAccountValueAdd (taxes, acc, amount);
                break;
            case GNC_AMT_TYPE_PERCENT:
                amount = gnc_numeric_div (amount, percent, GNC_DENOM_AUTO,
                                          GNC_HOW_DENOM_LCD);
                tax = gnc_numeric_mul (pretax, amount, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
                if (SCU) tax = gnc_numeric_convert(tax, SCU, GNC_HOW_RND_ROUND);
                taxes = gncAccountValueAdd (taxes, acc, tax);
                break;
            default:
                break;
            }
        }
        *tax_value = taxes;
    }

    return;
}
Пример #19
0
static void
check_add_subtract (void)
{
    int i;
    gnc_numeric a, b, c, d, z;

    a = gnc_numeric_create(2, 6);
    b = gnc_numeric_create(1, 4);

    /* Well, actually 14/24 would be acceptable/better in this case */
    check_binary_op (gnc_numeric_create(7, 12),
                     gnc_numeric_add(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT),
                     a, b, "expected %s got %s = %s + %s for add exact");

    check_binary_op (gnc_numeric_create(58, 100),
                     gnc_numeric_add(a, b, 100, GNC_HOW_RND_ROUND),
                     a, b, "expected %s got %s = %s + %s for add 100ths (banker's)");

    check_binary_op (gnc_numeric_create(5833, 10000),
                     gnc_numeric_add(a, b, GNC_DENOM_AUTO,
                                     GNC_HOW_DENOM_SIGFIGS(4) |
                                     GNC_HOW_RND_ROUND),
                     a, b, "expected %s got %s = %s + %s for add 4 sig figs");

    check_binary_op (gnc_numeric_create(583333, 1000000),
                     gnc_numeric_add(a, b, GNC_DENOM_AUTO,
                                     GNC_HOW_DENOM_SIGFIGS(6) |
                                     GNC_HOW_RND_ROUND),
                     a, b, "expected %s got %s = %s + %s for add 6 sig figs");

    check_binary_op (gnc_numeric_create(1, 12),
                     gnc_numeric_sub(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT),
                     a, b, "expected %s got %s = %s - %s for sub exact");

    /* We should try something trickier for reduce & lcd */
    check_binary_op (gnc_numeric_create(1, 12),
                     gnc_numeric_sub(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE),
                     a, b, "expected %s got %s = %s - %s for sub reduce");

    check_binary_op (gnc_numeric_create(1, 12),
                     gnc_numeric_sub(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD),
                     a, b, "expected %s got %s = %s - %s for sub reduce");

    check_binary_op (gnc_numeric_create(8, 100),
                     gnc_numeric_sub(a, b, 100, GNC_HOW_RND_ROUND),
                     a, b, "expected %s got %s = %s - %s for sub 100ths (banker's)");

    /* ------------------------------------------------------------ */
    /* This test has failed before */
    c = gnc_numeric_neg (a);
    d = gnc_numeric_neg (b);
    z = gnc_numeric_zero();
    check_binary_op (c, gnc_numeric_add_fixed(z, c),
                     z, c, "expected %s got %s = %s + %s for add fixed");

    check_binary_op (d, gnc_numeric_add_fixed(z, d),
                     z, d, "expected %s got %s = %s + %s for add fixed");

    /* ------------------------------------------------------------ */
    /* Same as above, but with signs reviersed */
    a = c;
    b = d;
    /* Well, actually 14/24 would be acceptable/better in this case */
    check_binary_op (gnc_numeric_create(-7, 12),
                     gnc_numeric_add(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT),
                     a, b, "expected %s got %s = %s + %s for add exact");

    check_binary_op (gnc_numeric_create(-58, 100),
                     gnc_numeric_add(a, b, 100, GNC_HOW_RND_ROUND),
                     a, b, "expected %s got %s = %s + %s for add 100ths (banker's)");

    check_binary_op (gnc_numeric_create(-5833, 10000),
                     gnc_numeric_add(a, b, GNC_DENOM_AUTO,
                                     GNC_HOW_DENOM_SIGFIGS(4) |
                                     GNC_HOW_RND_ROUND),
                     a, b, "expected %s got %s = %s + %s for add 4 sig figs");

    check_binary_op (gnc_numeric_create(-583333, 1000000),
                     gnc_numeric_add(a, b, GNC_DENOM_AUTO,
                                     GNC_HOW_DENOM_SIGFIGS(6) |
                                     GNC_HOW_RND_ROUND),
                     a, b, "expected %s got %s = %s + %s for add 6 sig figs");

    check_binary_op (gnc_numeric_create(-1, 12),
                     gnc_numeric_sub(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT),
                     a, b, "expected %s got %s = %s - %s for sub exact");

    /* We should try something trickier for reduce & lcd */
    check_binary_op (gnc_numeric_create(-1, 12),
                     gnc_numeric_sub(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE),
                     a, b, "expected %s got %s = %s - %s for sub reduce");

    check_binary_op (gnc_numeric_create(-1, 12),
                     gnc_numeric_sub(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD),
                     a, b, "expected %s got %s = %s - %s for sub reduce");

    check_binary_op (gnc_numeric_create(-8, 100),
                     gnc_numeric_sub(a, b, 100, GNC_HOW_RND_ROUND),
                     a, b, "expected %s got %s = %s - %s for sub 100ths (banker's)");

    /* ------------------------------------------------------------ */
    /* Add and subtract some random numbers */
    for (i = 0; i < NREPS; i++)
    {
        gnc_numeric e;
        gint64 deno = rand() + 1;
        gint64 na = get_random_gint64();
        gint64 nb = get_random_gint64();
        gint64 ne;

        /* avoid overflow; */
        na /= 2;
        nb /= 2;

        a = gnc_numeric_create(na, deno);
        b = gnc_numeric_create(nb, deno);

        /* Add */
        ne = na + nb;
        e = gnc_numeric_create(ne, deno);
        check_binary_op (e,
                         gnc_numeric_add(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT),
                         a, b, "expected %s got %s = %s + %s for exact addition");

        /* Subtract */
        ne = na - nb;
        e = gnc_numeric_create(ne, deno);
        check_binary_op (e,
                         gnc_numeric_sub(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT),
                         a, b, "expected %s got %s = %s - %s for exact subtraction");
    }
}
Пример #20
0
static void
check_reciprocal(void)
{
    gnc_numeric a, b, ans, val;
    double flo;

    val = gnc_numeric_create(-60, 20);
    check_unary_op (gnc_numeric_eq, gnc_numeric_create (-3, -1),
                    gnc_numeric_convert(val, GNC_DENOM_RECIPROCAL(1),
                                        GNC_HOW_RND_NEVER),
                    val, "expected %s got %s = (%s as RECIP(1))");

    a = gnc_numeric_create(200, 100);
    b = gnc_numeric_create(300, 100);

    /* 2 + 3 = 5 */
    ans = gnc_numeric_add(a, b, GNC_DENOM_RECIPROCAL(1), GNC_HOW_RND_NEVER);
    check_binary_op (gnc_numeric_create(5, -1),
                     ans, a, b, "expected %s got %s = %s + %s for reciprocal");

    /* 2 + 3 = 5 */
    a = gnc_numeric_create(2, -1);
    b = gnc_numeric_create(300, 100);
    ans = gnc_numeric_add(a, b, GNC_DENOM_RECIPROCAL(1), GNC_HOW_RND_NEVER);
    check_binary_op (gnc_numeric_create(5, -1),
                     ans, a, b, "expected %s got %s = %s + %s for reciprocal");

    /* check gnc_numeric_to_double */
    flo = gnc_numeric_to_double(gnc_numeric_create(5, -1));
    do_test ((5.0 == flo), "reciprocal conversion");

    /* check gnc_numeric_compare */
    a = gnc_numeric_create(2, 1);
    b = gnc_numeric_create(2, -1);
    do_test((0 == gnc_numeric_compare(a, b)), " 2 == 2 ");
    a = gnc_numeric_create(2, 1);
    b = gnc_numeric_create(3, -1);
    do_test((-1 == gnc_numeric_compare(a, b)), " 2 < 3 ");
    a = gnc_numeric_create(-2, 1);
    b = gnc_numeric_create(2, -1);
    do_test((-1 == gnc_numeric_compare(a, b)), " -2 < 2 ");
    a = gnc_numeric_create(2, -1);
    b = gnc_numeric_create(3, -1);
    do_test((-1 == gnc_numeric_compare(a, b)), " 2 < 3 ");

    /* check for equality */
    a = gnc_numeric_create(2, 1);
    b = gnc_numeric_create(2, -1);
    do_test(gnc_numeric_equal(a, b), " 2 == 2 ");

    /* check gnc_numeric_mul */
    a = gnc_numeric_create(2, 1);
    b = gnc_numeric_create(3, -1);
    ans = gnc_numeric_mul(a, b, GNC_DENOM_RECIPROCAL(1), GNC_HOW_RND_NEVER);
    check_binary_op (gnc_numeric_create(6, -1),
                     ans, a, b, "expected %s got %s = %s * %s for reciprocal");

    /* check gnc_numeric_div */
    /* -60 / 20 = -3 */
    a = gnc_numeric_create(-60, 1);
    b = gnc_numeric_create(2, -10);
    ans = gnc_numeric_div(a, b, GNC_DENOM_RECIPROCAL(1), GNC_HOW_RND_NEVER);
    check_binary_op (gnc_numeric_create(-3, -1),
                     ans, a, b, "expected %s got %s = %s / %s for reciprocal");

    /* 60 / 20 = 3 */
    a = gnc_numeric_create(60, 1);
    b = gnc_numeric_create(2, -10);
    ans = gnc_numeric_div(a, b, GNC_DENOM_RECIPROCAL(1), GNC_HOW_RND_NEVER);
    check_binary_op (gnc_numeric_create(3, -1),
                     ans, a, b, "expected %s got %s = %s / %s for reciprocal");


}
Пример #21
0
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 ("()");
}
/**
 * @fixme Move this non-GUI code into the engine.
 **/
static void
gnc_ui_accounts_recurse (Account *parent, GList **currency_list,
                         GNCSummarybarOptions options)
{
    gnc_numeric start_amount;
    gnc_numeric start_amount_default_currency;
    gnc_numeric end_amount;
    gnc_numeric end_amount_default_currency;
    GNCAccountType account_type;
    gnc_commodity * account_currency;
    gnc_commodity * euro_commodity;
    GNCCurrencyAcc *currency_accum = NULL;
    GNCCurrencyAcc *euro_accum = NULL;
    GNCCurrencyAcc *grand_total_accum = NULL;
    GNCCurrencyAcc *non_curr_accum = NULL;
    GList *children, *node;
    gboolean non_currency = FALSE;
    Timespec end_timespec;
    Timespec start_timespec;

    if (parent == NULL) return;

    children = gnc_account_get_children(parent);
    for (node = children; node; node = g_list_next(node))
    {
        Account *account = node->data;

        account_type = xaccAccountGetType(account);
        account_currency = xaccAccountGetCommodity(account);

        if (options.grand_total)
            grand_total_accum = gnc_ui_get_currency_accumulator(currency_list,
                                options.default_currency,
                                TOTAL_GRAND_TOTAL);

        if (options.euro)
        {
            euro_commodity = gnc_get_euro ();
            euro_accum = gnc_ui_get_currency_accumulator(currency_list,
                         euro_commodity,
                         TOTAL_CURR_TOTAL);
        }
        else
            euro_commodity = NULL;

        if (!gnc_commodity_is_currency(account_currency))
        {
            non_currency = TRUE;
            non_curr_accum = gnc_ui_get_currency_accumulator(currency_list,
                             options.default_currency,
                             TOTAL_NON_CURR_TOTAL);
        }

        if (!non_currency || options.non_currency)
        {
            currency_accum = gnc_ui_get_currency_accumulator(currency_list,
                             account_currency,
                             TOTAL_SINGLE);
        }

        switch (account_type)
        {
        case ACCT_TYPE_BANK:
        case ACCT_TYPE_CASH:
        case ACCT_TYPE_ASSET:
        case ACCT_TYPE_STOCK:
        case ACCT_TYPE_MUTUAL:
        case ACCT_TYPE_CREDIT:
        case ACCT_TYPE_LIABILITY:
        case ACCT_TYPE_PAYABLE:
        case ACCT_TYPE_RECEIVABLE:
            end_amount = xaccAccountGetBalanceAsOfDate(account, options.end_date);
            timespecFromTime_t(&end_timespec, options.end_date);
            end_amount_default_currency =
                xaccAccountConvertBalanceToCurrencyAsOfDate
                (account, end_amount, account_currency, options.default_currency,
                 timespecToTime_t(timespecCanonicalDayTime(end_timespec)));

            if (!non_currency || options.non_currency)
            {
                currency_accum->assets =
                    gnc_numeric_add (currency_accum->assets, end_amount,
                                     gnc_commodity_get_fraction (account_currency),
                                     GNC_HOW_RND_ROUND_HALF_UP);
            }

            if (non_currency)
            {
                non_curr_accum->assets =
                    gnc_numeric_add (non_curr_accum->assets, end_amount_default_currency,
                                     gnc_commodity_get_fraction (options.default_currency),
                                     GNC_HOW_RND_ROUND_HALF_UP);
            }

            if (options.grand_total)
            {
                grand_total_accum->assets =
                    gnc_numeric_add (grand_total_accum->assets, end_amount_default_currency,
                                     gnc_commodity_get_fraction (options.default_currency),
                                     GNC_HOW_RND_ROUND_HALF_UP);
            }

            if (options.euro && (currency_accum != euro_accum))
            {
                euro_accum->assets =
                    gnc_numeric_add (euro_accum->assets,
                                     gnc_convert_to_euro(account_currency, end_amount),
                                     gnc_commodity_get_fraction (euro_commodity),
                                     GNC_HOW_RND_ROUND_HALF_UP);
            }

            gnc_ui_accounts_recurse(account, currency_list, options);
            break;
        case ACCT_TYPE_INCOME:
        case ACCT_TYPE_EXPENSE:
            start_amount = xaccAccountGetBalanceAsOfDate(account, options.start_date);
            timespecFromTime_t(&start_timespec, options.start_date);
            start_amount_default_currency =
                xaccAccountConvertBalanceToCurrencyAsOfDate
                (account, start_amount, account_currency, options.default_currency,
                 timespecToTime_t(timespecCanonicalDayTime(start_timespec)));
            end_amount = xaccAccountGetBalanceAsOfDate(account, options.end_date);
            timespecFromTime_t(&end_timespec, options.end_date);
            end_amount_default_currency =
                xaccAccountConvertBalanceToCurrencyAsOfDate
                (account, end_amount, account_currency, options.default_currency,
                 timespecToTime_t(timespecCanonicalDayTime(end_timespec)));

            if (!non_currency || options.non_currency)
            {
                currency_accum->profits =
                    gnc_numeric_add (currency_accum->profits, start_amount,
                                     gnc_commodity_get_fraction (account_currency),
                                     GNC_HOW_RND_ROUND_HALF_UP);
                currency_accum->profits =
                    gnc_numeric_sub (currency_accum->profits, end_amount,
                                     gnc_commodity_get_fraction (account_currency),
                                     GNC_HOW_RND_ROUND_HALF_UP);
            }

            if (non_currency)
            {
                non_curr_accum->profits =
                    gnc_numeric_add (non_curr_accum->profits, start_amount_default_currency,
                                     gnc_commodity_get_fraction (options.default_currency),
                                     GNC_HOW_RND_ROUND_HALF_UP);
                non_curr_accum->profits =
                    gnc_numeric_sub (non_curr_accum->profits, end_amount_default_currency,
                                     gnc_commodity_get_fraction (options.default_currency),
                                     GNC_HOW_RND_ROUND_HALF_UP);
            }

            if (options.grand_total)
            {
                grand_total_accum->profits =
                    gnc_numeric_add (grand_total_accum->profits,
                                     start_amount_default_currency,
                                     gnc_commodity_get_fraction (options.default_currency),
                                     GNC_HOW_RND_ROUND_HALF_UP);
                grand_total_accum->profits =
                    gnc_numeric_sub (grand_total_accum->profits,
                                     end_amount_default_currency,
                                     gnc_commodity_get_fraction (options.default_currency),
                                     GNC_HOW_RND_ROUND_HALF_UP);
            }

            if (options.euro && (currency_accum != euro_accum))
            {
                euro_accum->profits =
                    gnc_numeric_add (euro_accum->profits,
                                     gnc_convert_to_euro(account_currency, start_amount),
                                     gnc_commodity_get_fraction (euro_commodity),
                                     GNC_HOW_RND_ROUND_HALF_UP);
                euro_accum->profits =
                    gnc_numeric_sub (euro_accum->profits,
                                     gnc_convert_to_euro(account_currency, end_amount),
                                     gnc_commodity_get_fraction (euro_commodity),
                                     GNC_HOW_RND_ROUND_HALF_UP);
            }

            gnc_ui_accounts_recurse(account, currency_list, options);
            break;
        case ACCT_TYPE_EQUITY:
            /* no-op, see comments at top about summing assets */
            break;
            /**
             * @fixme I don't know if this is right or if trading accounts should be
             *        treated like income and expense accounts.
             **/
        case ACCT_TYPE_TRADING:
            break;
        case ACCT_TYPE_CURRENCY:
        default:
            break;
        }
    }
    g_list_free(children);
}
Пример #23
0
void
xaccLotScrubDoubleBalance (GNCLot *lot)
{
    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"));
}