Beispiel #1
0
static void
check_rounding (void)
{
    gnc_numeric val;

    val = gnc_numeric_create(7, 16);
    check_unary_op (gnc_numeric_eq,
                    gnc_numeric_create (43, 100),
                    gnc_numeric_convert (val, 100, GNC_HOW_RND_FLOOR),
                    val, "expected %s got %s = (%s as 100th's floor)");
    check_unary_op (gnc_numeric_eq,
                    gnc_numeric_create (44, 100),
                    gnc_numeric_convert (val, 100, GNC_HOW_RND_CEIL),
                    val, "expected %s got %s = (%s as 100th's ceiling)");
    check_unary_op (gnc_numeric_eq,
                    gnc_numeric_create (43, 100),
                    gnc_numeric_convert (val, 100, GNC_HOW_RND_TRUNC),
                    val, "expected %s got %s = (%s as 100th's trunc)");
    check_unary_op (gnc_numeric_eq,
                    gnc_numeric_create (44, 100),
                    gnc_numeric_convert (val, 100, GNC_HOW_RND_ROUND),
                    val, "expected %s got %s = (%s as 100th's round)");

    val = gnc_numeric_create(1511, 1000);
    check_unary_op (gnc_numeric_eq,
                    gnc_numeric_create (151, 100),
                    gnc_numeric_convert (val, 100, GNC_HOW_RND_ROUND),
                    val, "expected %s got %s = (%s as 100th's round)");

    val = gnc_numeric_create(1516, 1000);
    check_unary_op (gnc_numeric_eq,
                    gnc_numeric_create (152, 100),
                    gnc_numeric_convert (val, 100, GNC_HOW_RND_ROUND),
                    val, "expected %s got %s = (%s as 100th's round)");

    /* Half-values always get rounded to nearest even number */
    val = gnc_numeric_create(1515, 1000);
    check_unary_op (gnc_numeric_eq,
                    gnc_numeric_create (152, 100),
                    gnc_numeric_convert (val, 100, GNC_HOW_RND_ROUND),
                    val, "expected %s got %s = (%s as 100th's round)");

    val = gnc_numeric_create(1525, 1000);
    check_unary_op (gnc_numeric_eq,
                    gnc_numeric_create (152, 100),
                    gnc_numeric_convert (val, 100, GNC_HOW_RND_ROUND),
                    val, "expected %s got %s = (%s as 100th's round)");

    val = gnc_numeric_create(1535, 1000);
    check_unary_op (gnc_numeric_eq,
                    gnc_numeric_create (154, 100),
                    gnc_numeric_convert (val, 100, GNC_HOW_RND_ROUND),
                    val, "expected %s got %s = (%s as 100th's round)");

    val = gnc_numeric_create(1545, 1000);
    check_unary_op (gnc_numeric_eq,
                    gnc_numeric_create (154, 100),
                    gnc_numeric_convert (val, 100, GNC_HOW_RND_ROUND),
                    val, "expected %s got %s = (%s as 100th's round)");
}
Beispiel #2
0
int
gnc_numeric_same(gnc_numeric a, gnc_numeric b, gint64 denom,
                 gint how)
{
    gnc_numeric aconv, bconv;

    aconv = gnc_numeric_convert(a, denom, how);
    bconv = gnc_numeric_convert(b, denom, how);

    return(gnc_numeric_equal(aconv, bconv));
}
static void
test_num (gnc_numeric n)
{
    GNCPrintAmountInfo print_info;
    int fraction;
    int i;

    print_info.commodity = NULL;
    print_info.min_decimal_places = 0;
    print_info.use_locale = 1;
    print_info.use_symbol = 0;

    for (i = 1, fraction = 10; i < 9; i++, fraction *= 10)
    {
        gnc_numeric n1;

        print_info.use_separators = 1;
        print_info.monetary = 1;
        print_info.max_decimal_places = i;
        print_info.force_fit = 0;
        print_info.round = 0;

        n1 = gnc_numeric_convert (n, fraction, GNC_HOW_RND_ROUND_HALF_UP);
        if (gnc_numeric_check(n1))
        {
            do_test_args((gnc_numeric_check(n1) == GNC_ERROR_OVERFLOW),
                         "BAD NUMERIC CONVERSION", __FILE__, __LINE__,
                         "num: %s, fraction: %d", gnc_numeric_to_string(n), fraction);
            continue;
        }

        test_num_print_info (n1, print_info, __LINE__);

        print_info.monetary = 0;
        test_num_print_info (n1, print_info, __LINE__);

        print_info.use_separators = 0;
        test_num_print_info (n1, print_info, __LINE__);

        print_info.round = 1;
        test_num_print_info (n1, print_info, __LINE__);

        print_info.round = 0;
        print_info.force_fit = 1;
        test_num_print_info (n1, print_info, __LINE__);

        print_info.round = 1;
        test_num_print_info (n1, print_info, __LINE__);
    }
}
Beispiel #4
0
SCM gnc_account_value_ptr_to_scm (GncAccountValue *av)
{
    swig_type_info * account_type = get_acct_type();
    gnc_commodity * com;
    gnc_numeric val;

    if (!av) return SCM_BOOL_F;

    com = xaccAccountGetCommodity (av->account);
    val = gnc_numeric_convert (av->value, gnc_commodity_get_fraction (com),
                               GNC_HOW_RND_ROUND_HALF_UP);

    return scm_cons (SWIG_NewPointerObj(av->account, account_type, 0),
                     gnc_numeric_to_scm (val));
}
Beispiel #5
0
static gint
gnc_price_cell_parse (PriceCell *cell, gboolean update_value)
{
    const char *newval;
    char *oldval;
    gnc_numeric amount;

    if (!cell->need_to_parse)
        return -1;

    oldval = cell->cell.value;
    if (oldval == NULL)
        oldval = "";

    {
        char *err_location = NULL;
        if (strlen(g_strstrip(cell->cell.value)) == 0)
        {
            cell->amount = gnc_numeric_zero ();
        }
        else if (gnc_exp_parser_parse (cell->cell.value, &amount, &err_location))
        {
            if (cell->fraction > 0)
                amount = gnc_numeric_convert (amount, cell->fraction, GNC_HOW_RND_ROUND);

            cell->amount = amount;
        }
        else
        {
            return (err_location - cell->cell.value);
        }
    }

    if (!update_value)
        return -1;

    newval = gnc_price_cell_print_value (cell);

    /* If they are identical do nothing */
    if (strcmp(newval, oldval) == 0)
        return -1;

    /* Otherwise, change it */
    gnc_basic_cell_set_value_internal (&cell->cell, newval);
    return -1;
}
Beispiel #6
0
gboolean
gnc_price_cell_set_value (PriceCell * cell, gnc_numeric amount)
{
    const char *buff;

    if (cell == NULL)
        return FALSE;

    if (cell->fraction > 0)
        amount = gnc_numeric_convert (amount, cell->fraction, GNC_HOW_RND_ROUND);

    cell->amount = amount;
    buff = gnc_price_cell_print_value (cell);
    cell->need_to_parse = FALSE;

    if (safe_strcmp (buff, cell->cell.value) == 0)
        return FALSE;

    gnc_basic_cell_set_value_internal (&cell->cell, buff);

    return TRUE;
}
static void
balance_cell_edited (GtkCellRendererText *cell,
                     gchar               *path,
                     gchar               *new_text,
                     gpointer             user_data)
{
    Account *account;
    char *error_loc;
    gnc_numeric amount;
    hierarchy_data *data = (hierarchy_data *)user_data;

    g_return_if_fail(data != NULL);

    account = gnc_tree_view_account_get_selected_account(data->final_account_tree);
    if (account == NULL)
    {
        g_critical("account is null");
        return;
    }

    error_loc = NULL;
    if (!gnc_exp_parser_parse (new_text, &amount, &error_loc))
    {
        amount = gnc_numeric_zero();
        g_object_set (G_OBJECT(cell), "text", "", NULL);
    }
    /* Bug#348364: Emulating price-cell, we need to ensure the denominator of
     * the amount is in the SCU of the account's commodity (so
     * gnc-ui-util.c:is_decimal_fraction() on the remainder denom for
     * fractional values will be a "decimal").
     */
    {
        int account_cmdty_fraction = xaccAccountGetCommoditySCU(account);
        amount = gnc_numeric_convert(amount, account_cmdty_fraction, GNC_HOW_RND_ROUND_HALF_UP);
    }
    set_final_balance (data->balance_hash, account, amount);
    qof_event_gen (QOF_INSTANCE(account), QOF_EVENT_MODIFY, NULL);
}
/***********************************************************************
 * @todo Maybe invoice checking should be done in gnc_bi_import_fix_bis (...)
 * rather than in here?  But that is more concerned with ensuring the csv is consistent.
 * @param GtkListStore *store
 * @param guint *n_invoices_created
 * @param guint *n_invoices_updated
 * @return void
 ***********************************************************************/
void
gnc_bi_import_create_bis (GtkListStore * store, QofBook * book,
                          guint * n_invoices_created,
                          guint * n_invoices_updated,
                          gchar * type, gchar * open_mode, GString * info)
{
    gboolean valid;
    GtkTreeIter iter;
    gchar *id = NULL, *date_opened = NULL, *owner_id = NULL, *billing_id = NULL, *notes = NULL;
    gchar *date = NULL, *desc = NULL, *action = NULL, *account = NULL, *quantity = NULL,
          *price = NULL, *disc_type = NULL, *disc_how = NULL, *discount = NULL, *taxable = NULL,
          *taxincluded = NULL, *tax_table = NULL;
    gchar *date_posted = NULL, *due_date = NULL, *account_posted = NULL, *memo_posted = NULL,
          *accumulatesplits = NULL;
    guint dummy;
    GncInvoice *invoice;
    GncEntry *entry;
    gint day, month, year;
    gnc_numeric value;
    GncOwner *owner;
    Account *acc;
    enum update {YES = GTK_RESPONSE_YES, NO = GTK_RESPONSE_NO} update;
    GtkWidget *dialog;
    Timespec today;
    InvoiceWindow *iw;
    gchar *new_id = NULL;
    gint64 denom = 0;
    gnc_commodity *currency;

    // these arguments are needed
    g_return_if_fail (store && book);
    // logic of this function only works for bills or invoices
    g_return_if_fail ((g_ascii_strcasecmp (type, "INVOICE") == 0) ||
            (g_ascii_strcasecmp (type, "BILL") == 0));

    // allow to call this function without statistics
    if (!n_invoices_created)
        n_invoices_created = &dummy;
    if (!n_invoices_updated)
        n_invoices_updated = &dummy;
    *n_invoices_created = 0;
    *n_invoices_updated = 0;

    invoice = NULL;
    update = NO;

    valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter);
    while (valid)
    {
        // Walk through the list, reading each row
        gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
                            ID, &id,
                            DATE_OPENED, &date_opened,
                            DATE_POSTED, &date_posted,       // if autoposting requested
                            DUE_DATE, &due_date,             // if autoposting requested
                            ACCOUNT_POSTED, &account_posted, // if autoposting requested
                            MEMO_POSTED, &memo_posted,       // if autoposting requested
                            ACCU_SPLITS, &accumulatesplits,  // if autoposting requested
                            OWNER_ID, &owner_id,
                            BILLING_ID, &billing_id,
                            NOTES, &notes,
                            DATE, &date,
                            DESC, &desc,
                            ACTION, &action,
                            ACCOUNT, &account,
                            QUANTITY, &quantity,
                            PRICE, &price,
                            DISC_TYPE, &disc_type,
                            DISC_HOW, &disc_how,
                            DISCOUNT, &discount,
                            TAXABLE, &taxable,
                            TAXINCLUDED, &taxincluded,
                            TAX_TABLE, &tax_table, -1);

        // TODO:  Assign a new invoice number if one is absent.  BUT we don't want to assign a new invoice for every line!!
        // so we'd have to flag this up somehow or add an option in the import GUI.  The former implies that we make
        // an assumption about what the importer (person) wants to do.  It seems reasonable that a CSV file full of items with
        // If an invoice exists then we add to it in this current schema.
        // no predefined invoice number is a new invoice that's in need of a new number.
        // This was  not designed to satisfy the need for repeat invoices however, so maybe we need a another method for this, after all
        // It should be easier to copy an invoice with a new ID than to go through all this malarky.
        if (g_ascii_strcasecmp (type, "BILL") == 0)
            invoice = gnc_search_bill_on_id (book, id);
        else if (g_ascii_strcasecmp (type, "INVOICE") == 0)
            invoice = gnc_search_invoice_on_id (book, id);
        DEBUG( "Existing %s ID: %s\n", type, gncInvoiceGetID(invoice));

        // If the search is empty then there is no existing invoice so make a new one
        if (invoice == NULL)
        {
             DEBUG( "Creating a new : %s\n", type );
            // new invoice
            invoice = gncInvoiceCreate (book);
            /* Protect against thrashing the DB and trying to write the invoice
             * record prematurely */
            gncInvoiceBeginEdit (invoice);
            gncInvoiceSetID (invoice, id);
            owner = gncOwnerNew ();
            if (g_ascii_strcasecmp (type, "BILL") == 0)
                gncOwnerInitVendor (owner,
                                    gnc_search_vendor_on_id (book, owner_id));
            else if (g_ascii_strcasecmp (type, "INVOICE") == 0)
                gncOwnerInitCustomer (owner,
                                      gnc_search_customer_on_id (book, owner_id));
            gncInvoiceSetOwner (invoice, owner);
            gncInvoiceSetCurrency (invoice, gncOwnerGetCurrency (owner));	// Set the invoice currency based on the owner
            if (strlen (date_opened) != 0)	// If a date is specified in CSV
            {
                // FIXME: Must check for the return value of qof_scan_date!
                qof_scan_date (date_opened, &day, &month, &year);
                gncInvoiceSetDateOpened (invoice,
                                         gnc_dmy2timespec (day, month, year));
            }
            else			// If no date in CSV
            {
                time64 now = gnc_time (NULL);
                Timespec now_timespec;
                timespecFromTime64 (&now_timespec, now);
                gncInvoiceSetDateOpened (invoice, now_timespec);
            }
            gncInvoiceSetBillingID (invoice, billing_id ? billing_id : "");
            notes = un_escape(notes);
            gncInvoiceSetNotes (invoice, notes ? notes : "");
            gncInvoiceSetActive (invoice, TRUE);
            //if (g_ascii_strcasecmp(type,"INVOICE"))gncInvoiceSetBillTo( invoice, billto );
            (*n_invoices_created)++;
            update = YES;

            // open new bill / invoice in a tab, if requested
            if (g_ascii_strcasecmp(open_mode, "ALL") == 0
                    || (g_ascii_strcasecmp(open_mode, "NOT_POSTED") == 0
                        && strlen(date_posted) == 0))
            {
                iw =  gnc_ui_invoice_edit (invoice);
                gnc_plugin_page_invoice_new (iw);
            }
            gncInvoiceCommitEdit (invoice);
        }
// I want to warn the user that an existing billvoice exists, but not every
// time.
// An import can contain many lines usually referring to the same invoice.
// NB: Posted invoices are NEVER updated.
        else			// if invoice exists
        {
            if (gncInvoiceIsPosted (invoice))	// Is it already posted?
            {
                valid =
                    gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);
                continue;		// If already posted then never import
            }
            if (update != YES)	// Pop up a dialog to ask if updates are the expected action
            {
                dialog = gtk_message_dialog_new (NULL,
                                                 GTK_DIALOG_MODAL,
                                                 GTK_MESSAGE_ERROR,
                                                 GTK_BUTTONS_YES_NO,
                                                 "%s",
                                                 _("Are you sure you have bills/invoices to update?"));
                update = gtk_dialog_run (GTK_DIALOG (dialog));
                gtk_widget_destroy (dialog);
                if (update == NO)
                {
                    // Cleanup and leave
                    g_free (id);
                    g_free (date_opened);
                    g_free (owner_id);
                    g_free (billing_id);
                    g_free (notes);
                    g_free (date);
                    g_free (desc);
                    g_free (action);
                    g_free (account);
                    g_free (quantity);
                    g_free (price);
                    g_free (disc_type);
                    g_free (disc_how);
                    g_free (discount);
                    g_free (taxable);
                    g_free (taxincluded);
                    g_free (tax_table);
                    g_free (date_posted);
                    g_free (due_date);
                    g_free (account_posted);
                    g_free (memo_posted);
                    g_free (accumulatesplits);
                    return;
                }
            }
            (*n_invoices_updated)++;
        }


        // add entry to invoice/bill
        entry = gncEntryCreate (book);
        gncEntryBeginEdit(entry);
        currency = gncInvoiceGetCurrency(invoice);
        if (currency) denom = gnc_commodity_get_fraction(currency);
        // FIXME: Must check for the return value of qof_scan_date!
        qof_scan_date (date, &day, &month, &year);
        {
            GDate *date = g_date_new_dmy(day, month, year);
            gncEntrySetDateGDate (entry, date);
            g_date_free (date);
        }
        timespecFromTime64 (&today, gnc_time (NULL));	// set today to the current date
        gncEntrySetDateEntered (entry, today);
        // Remove escaped quotes
        desc = un_escape(desc);
        notes = un_escape(notes);
        gncEntrySetDescription (entry, desc);
        gncEntrySetAction (entry, action);
        value = gnc_numeric_zero(); 
        gnc_exp_parser_parse (quantity, &value, NULL);
        // Need to set the denom appropriately else we get stupid rounding errors.
        value = gnc_numeric_convert (value, denom * 100, GNC_HOW_RND_NEVER);
        //DEBUG("qty = %s",gnc_num_dbg_to_string(value));
        gncEntrySetQuantity (entry, value);
        acc = gnc_account_lookup_for_register (gnc_get_current_root_account (),
                                               account);

        if (g_ascii_strcasecmp (type, "BILL") == 0)
        {
            gncEntrySetBillAccount (entry, acc);
            value = gnc_numeric_zero();
            gnc_exp_parser_parse (price, &value, NULL);
            value = gnc_numeric_convert (value, denom * 100, GNC_HOW_RND_NEVER);
            gncEntrySetBillPrice (entry, value);
            gncEntrySetBillTaxable (entry, text2bool (taxable));
            gncEntrySetBillTaxIncluded (entry, text2bool (taxincluded));
            gncEntrySetBillTaxTable (entry, gncTaxTableLookupByName (book, tax_table));
            gncEntryCommitEdit(entry);
            gncBillAddEntry (invoice, entry);
        }
        else if (g_ascii_strcasecmp (type, "INVOICE") == 0)
        {
            gncEntrySetNotes (entry, notes);
            gncEntrySetInvAccount (entry, acc);
            value = gnc_numeric_zero();
            gnc_exp_parser_parse (price, &value, NULL);
            value = gnc_numeric_convert (value, denom * 100, GNC_HOW_RND_NEVER);
            //DEBUG("price = %s",gnc_num_dbg_to_string(value));
            gncEntrySetInvPrice (entry, value);
            gncEntrySetInvTaxable (entry, text2bool (taxable));
            gncEntrySetInvTaxIncluded (entry, text2bool (taxincluded));
            gncEntrySetInvTaxTable (entry, gncTaxTableLookupByName (book, tax_table));
            value = gnc_numeric_zero();
            gnc_exp_parser_parse (discount, &value, NULL);
            value = gnc_numeric_convert (value, denom * 100, GNC_HOW_RND_NEVER);
            gncEntrySetInvDiscount (entry, value);
            gncEntrySetInvDiscountType (entry, text2disc_type (disc_type));
            gncEntrySetInvDiscountHow (entry, text2disc_how (disc_how));
            gncEntryCommitEdit(entry);
            gncInvoiceAddEntry (invoice, entry);
        }
        valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);
        // handle auto posting of invoices

        new_id = NULL;
       
        if (valid)
            gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, ID, &new_id, -1);
        if (g_strcmp0 (id, new_id) != 0)
        {
            // the next invoice id is different => try to autopost this invoice
            if (qof_scan_date (date_posted, &day, &month, &year))
            {
                // autopost this invoice
                gboolean auto_pay;
                Timespec d1, d2;

                if (g_ascii_strcasecmp (type, "INVOICE") == 0)
                    auto_pay = gnc_prefs_get_bool (GNC_PREFS_GROUP_INVOICE, GNC_PREF_AUTO_PAY);
                else
                    auto_pay = gnc_prefs_get_bool (GNC_PREFS_GROUP_BILL, GNC_PREF_AUTO_PAY);

                d1 = gnc_dmy2timespec (day, month, year);
                // FIXME: Must check for the return value of qof_scan_date!
                qof_scan_date (due_date, &day, &month, &year);	// obtains the due date, or leaves it at date_posted
                d2 = gnc_dmy2timespec (day, month, year);
                acc = gnc_account_lookup_for_register
                      (gnc_get_current_root_account (), account_posted);
                gncInvoicePostToAccount (invoice, acc, &d1, &d2,
                                         memo_posted,
                                         text2bool (accumulatesplits),
                                         auto_pay);
                DEBUG("Invoice %s posted",id);
            }

        }


    }
    // cleanup
    g_free (new_id);
    g_free (id);
    g_free (date_opened);
    g_free (owner_id);
    g_free (billing_id);
    g_free (notes);
    g_free (date);
    g_free (desc);
    g_free (action);
    g_free (account);
    g_free (quantity);
    g_free (price);
    g_free (disc_type);
    g_free (disc_how);
    g_free (discount);
    g_free (taxable);
    g_free (taxincluded);
    g_free (tax_table);
    g_free (date_posted);
    g_free (due_date);
    g_free (account_posted);
    g_free (memo_posted);
    g_free (accumulatesplits);

}
Beispiel #9
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;
}
Beispiel #10
0
static void
gncEntryRecomputeValues (GncEntry *entry)
{
    int denom;

    /* See if either tax table changed since we last computed values */
    if (entry->i_tax_table)
    {
        Timespec modtime = gncTaxTableLastModified (entry->i_tax_table);
        if (timespec_cmp (&entry->i_taxtable_modtime, &modtime))
        {
            entry->values_dirty = TRUE;
            entry->i_taxtable_modtime = modtime;
        }
    }
    if (entry->b_tax_table)
    {
        Timespec modtime = gncTaxTableLastModified (entry->b_tax_table);
        if (timespec_cmp (&entry->b_taxtable_modtime, &modtime))
        {
            entry->values_dirty = TRUE;
            entry->b_taxtable_modtime = modtime;
        }
    }

    if (!entry->values_dirty)
        return;

    /* Clear the last-computed tax values */
    if (entry->i_tax_values)
    {
        gncAccountValueDestroy (entry->i_tax_values);
        entry->i_tax_values = NULL;
    }
    if (entry->b_tax_values)
    {
        gncAccountValueDestroy (entry->b_tax_values);
        entry->b_tax_values = NULL;
    }

    /* Determine the commodity denominator */
    denom = get_entry_commodity_denom (entry);

    /* Compute the invoice values */
    gncEntryComputeValue (entry->quantity, entry->i_price,
                          (entry->i_taxable ? entry->i_tax_table : NULL),
                          entry->i_taxincluded,
                          entry->i_discount, entry->i_disc_type,
                          entry->i_disc_how,
                          denom,
                          &(entry->i_value), &(entry->i_disc_value),
                          &(entry->i_tax_values));

    /* Compute the bill values */
    gncEntryComputeValue (entry->quantity, entry->b_price,
                          (entry->b_taxable ? entry->b_tax_table : NULL),
                          entry->b_taxincluded,
                          gnc_numeric_zero(), GNC_AMT_TYPE_VALUE, GNC_DISC_PRETAX,
                          denom,
                          &(entry->b_value), NULL, &(entry->b_tax_values));

    entry->i_value_rounded = gnc_numeric_convert (entry->i_value, denom,
                             GNC_HOW_RND_ROUND);
    entry->i_disc_value_rounded = gnc_numeric_convert (entry->i_disc_value, denom,
                                  GNC_HOW_RND_ROUND);
    entry->i_tax_value = gncAccountValueTotal (entry->i_tax_values);
    entry->i_tax_value_rounded = gnc_numeric_convert (entry->i_tax_value, denom,
                                 GNC_HOW_RND_ROUND);

    entry->b_value_rounded = gnc_numeric_convert (entry->b_value, denom,
                             GNC_HOW_RND_ROUND);
    entry->b_tax_value = gncAccountValueTotal (entry->b_tax_values);
    entry->b_tax_value_rounded = gnc_numeric_convert (entry->b_tax_value, denom,
                                 GNC_HOW_RND_ROUND);
    entry->values_dirty = FALSE;
}
Beispiel #11
0
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;
    }
}
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");


}
Beispiel #13
0
gnc_numeric
gnc_numeric_div(gnc_numeric a, gnc_numeric b,
                gint64 denom, gint how)
{
    gnc_numeric quotient;
    qofint128 nume, deno;

    if (gnc_numeric_check(a) || gnc_numeric_check(b))
    {
        return gnc_numeric_error(GNC_ERROR_ARG);
    }

    if ((denom == GNC_DENOM_AUTO) &&
            (how & GNC_NUMERIC_DENOM_MASK) == GNC_HOW_DENOM_FIXED)
    {
        if (a.denom == b.denom)
        {
            denom = a.denom;
        }
        else if (a.denom == 0)
        {
            denom = b.denom;
        }
        else
        {
            return gnc_numeric_error(GNC_ERROR_DENOM_DIFF);
        }
    }


    if (a.denom < 0)
    {
        a.num *= -a.denom;   /* BUG: overflow not handled.  */
        a.denom = 1;
    }

    if (b.denom < 0)
    {
        b.num *= -b.denom;   /* BUG: overflow not handled.  */
        b.denom = 1;
    }

    if (a.denom == b.denom)
    {
        quotient.num = a.num;
        quotient.denom = b.num;
    }
    else
    {
        gint64 sgn = 1;
        if (0 > a.num)
        {
            sgn = -sgn;
            a.num = -a.num;
        }
        if (0 > b.num)
        {
            sgn = -sgn;
            b.num = -b.num;
        }
        nume = mult128(a.num, b.denom);
        deno = mult128(b.num, a.denom);

        /* Try to avoid overflow by removing common factors */
        if (nume.isbig && deno.isbig)
        {
            gnc_numeric ra = gnc_numeric_reduce (a);
            gnc_numeric rb = gnc_numeric_reduce (b);

            gint64 gcf_nume = gcf64(ra.num, rb.num);
            gint64 gcf_deno = gcf64(rb.denom, ra.denom);
            nume = mult128(ra.num / gcf_nume, rb.denom / gcf_deno);
            deno = mult128(rb.num / gcf_nume, ra.denom / gcf_deno);
        }

        if ((0 == nume.isbig) && (0 == deno.isbig))
        {
            quotient.num = sgn * nume.lo;
            quotient.denom = deno.lo;
            goto dive_done;
        }
        else if (0 == deno.isbig)
        {
            quotient = reduce128 (nume, deno.lo);
            if (0 == gnc_numeric_check (quotient))
            {
                quotient.num *= sgn;
                goto dive_done;
            }
        }

        /* If rounding allowed, then shift until there's no
         * more overflow. The conversion at the end will fix
         * things up for the final value. */
        if ((how & GNC_NUMERIC_RND_MASK) == GNC_HOW_RND_NEVER)
        {
            return gnc_numeric_error (GNC_ERROR_OVERFLOW);
        }
        while (nume.isbig || deno.isbig)
        {
            nume = shift128 (nume);
            deno = shift128 (deno);
        }
        quotient.num = sgn * nume.lo;
        quotient.denom = deno.lo;
        if (0 == quotient.denom)
        {
            return gnc_numeric_error (GNC_ERROR_OVERFLOW);
        }
    }

    if (quotient.denom < 0)
    {
        quotient.num   = -quotient.num;
        quotient.denom = -quotient.denom;
    }

dive_done:
    if ((denom == GNC_DENOM_AUTO) &&
            ((how & GNC_NUMERIC_DENOM_MASK) == GNC_HOW_DENOM_LCD))
    {
        denom = gnc_numeric_lcd(a, b);
        how   = how & GNC_NUMERIC_RND_MASK;
    }

    return gnc_numeric_convert(quotient, denom, how);
}
Beispiel #14
0
gnc_numeric
gnc_numeric_mul(gnc_numeric a, gnc_numeric b,
                gint64 denom, gint how)
{
    gnc_numeric product, result;
    qofint128 bignume, bigdeno;

    if (gnc_numeric_check(a) || gnc_numeric_check(b))
    {
        return gnc_numeric_error(GNC_ERROR_ARG);
    }

    if ((denom == GNC_DENOM_AUTO) &&
            (how & GNC_NUMERIC_DENOM_MASK) == GNC_HOW_DENOM_FIXED)
    {
        if (a.denom == b.denom)
        {
            denom = a.denom;
        }
        else if (b.num == 0)
        {
            denom = a.denom;
        }
        else if (a.num == 0)
        {
            denom = b.denom;
        }
        else
        {
            return gnc_numeric_error(GNC_ERROR_DENOM_DIFF);
        }
    }

    if ((denom == GNC_DENOM_AUTO) &&
            ((how & GNC_NUMERIC_DENOM_MASK) == GNC_HOW_DENOM_LCD))
    {
        denom = gnc_numeric_lcd(a, b);
        how   = how & GNC_NUMERIC_RND_MASK;
    }

    if (a.denom < 0)
    {
        a.num *= -a.denom;  /* BUG: overflow not handled.  */
        a.denom = 1;
    }

    if (b.denom < 0)
    {
        b.num *= -b.denom;  /* BUG: overflow not handled.  */
        b.denom = 1;
    }

    bignume = mult128 (a.num, b.num);
    bigdeno = mult128 (a.denom, b.denom);
    product.num   = a.num * b.num;
    product.denom = a.denom * b.denom;

    /* If it looks to be overflowing, try to reduce the fraction ... */
    if (bignume.isbig || bigdeno.isbig)
    {
        gint64 tmp;
        a = gnc_numeric_reduce (a);
        b = gnc_numeric_reduce (b);
        tmp = a.num;
        a.num = b.num;
        b.num = tmp;
        a = gnc_numeric_reduce (a);
        b = gnc_numeric_reduce (b);

        bignume = mult128 (a.num, b.num);
        bigdeno = mult128 (a.denom, b.denom);
        product.num   = a.num * b.num;
        product.denom = a.denom * b.denom;
    }

    /* If it its still overflowing, and rounding is allowed then round */
    if (bignume.isbig || bigdeno.isbig)
    {
        /* If rounding allowed, then shift until there's no
         * more overflow. The conversion at the end will fix
         * things up for the final value. Else overflow. */
        if ((how & GNC_NUMERIC_RND_MASK) == GNC_HOW_RND_NEVER)
        {
            if (bigdeno.isbig)
            {
                return gnc_numeric_error (GNC_ERROR_OVERFLOW);
            }
            product = reduce128 (bignume, product.denom);
            if (gnc_numeric_check (product))
            {
                return gnc_numeric_error (GNC_ERROR_OVERFLOW);
            }
        }
        else
        {
            while (bignume.isbig || bigdeno.isbig)
            {
                bignume = shift128 (bignume);
                bigdeno = shift128 (bigdeno);
            }
            product.num = bignume.lo;
            if (bignume.isneg) product.num = -product.num;

            product.denom = bigdeno.lo;
            if (0 == product.denom)
            {
                return gnc_numeric_error (GNC_ERROR_OVERFLOW);
            }
        }
    }

    result = gnc_numeric_convert(product, denom, how);
    return result;
}
Beispiel #15
0
gnc_numeric
gnc_numeric_add(gnc_numeric a, gnc_numeric b,
                gint64 denom, gint how)
{
    gnc_numeric sum;

    if (gnc_numeric_check(a) || gnc_numeric_check(b))
    {
        return gnc_numeric_error(GNC_ERROR_ARG);
    }

    if ((denom == GNC_DENOM_AUTO) &&
            (how & GNC_NUMERIC_DENOM_MASK) == GNC_HOW_DENOM_FIXED)
    {
        if (a.denom == b.denom)
        {
            denom = a.denom;
        }
        else if (b.num == 0)
        {
            denom = a.denom;
            b.denom = a.denom;
        }
        else if (a.num == 0)
        {
            denom = b.denom;
            a.denom = b.denom;
        }
        else
        {
            return gnc_numeric_error(GNC_ERROR_DENOM_DIFF);
        }
    }

    if (a.denom < 0)
    {
        a.num *= -a.denom;  /* BUG: overflow not handled.  */
        a.denom = 1;
    }

    if (b.denom < 0)
    {
        b.num *= -b.denom;  /* BUG: overflow not handled.  */
        b.denom = 1;
    }

    /* Get an exact answer.. same denominator is the common case. */
    if (a.denom == b.denom)
    {
        sum.num = a.num + b.num;  /* BUG: overflow not handled.  */
        sum.denom = a.denom;
    }
    else
    {
        /* We want to do this:
         *    sum.num = a.num*b.denom + b.num*a.denom;
         *    sum.denom = a.denom*b.denom;
         * but the multiply could overflow.
         * Computing the LCD minimizes likelihood of overflow
         */
        gint64 lcd;
        qofint128 ca, cb, cab;
        lcd = gnc_numeric_lcd(a, b);
        if (GNC_ERROR_ARG == lcd)
        {
            return gnc_numeric_error(GNC_ERROR_OVERFLOW);
        }
        ca = mult128 (a.num, lcd / a.denom);
        if (ca.isbig) return gnc_numeric_error(GNC_ERROR_OVERFLOW);

        cb = mult128 (b.num, lcd / b.denom);
        if (cb.isbig) return gnc_numeric_error(GNC_ERROR_OVERFLOW);

        cab = add128 (ca, cb);
        if (cab.isbig) return gnc_numeric_error(GNC_ERROR_OVERFLOW);

        sum.num   = cab.lo;
        if (cab.isneg) sum.num = -sum.num;
        sum.denom = lcd;
    }

    if ((denom == GNC_DENOM_AUTO) &&
            ((how & GNC_NUMERIC_DENOM_MASK) == GNC_HOW_DENOM_LCD))
    {
        denom = gnc_numeric_lcd(a, b);
        how   = how & GNC_NUMERIC_RND_MASK;
    }

    return gnc_numeric_convert(sum, denom, how);
}
void
gnc_autoclear_window_ok_cb (GtkWidget *widget,
                            AutoClearWindow *data)
{
    GList *node, *nc_list = 0, *toclear_list = 0;
    gnc_numeric toclear_value;
    GHashTable *sack;

    gtk_label_set_text(data->status_label, _("Searching for splits to clear ..."));

    /* Value we have to reach */
    toclear_value = gnc_amount_edit_get_amount(data->end_value);
    toclear_value = gnc_numeric_convert(toclear_value, xaccAccountGetCommoditySCU(data->account), GNC_HOW_RND_NEVER);

    /* Extract which splits are not cleared and compute the amount we have to clear */
    for (node = xaccAccountGetSplitList(data->account); node; node = node->next)
    {
        Split *split = (Split *)node->data;
        char recn;
        gnc_numeric value;

        recn = xaccSplitGetReconcile (split);
        value = xaccSplitGetAmount (split);

        if (recn == NREC)
            nc_list = g_list_append(nc_list, split);
        else
            toclear_value = gnc_numeric_sub_fixed(toclear_value, value);
    }

    /* Pretty print information */
    PINFO("Amount to clear: %s\n", gnc_numeric_to_string(toclear_value));
    PINFO("Available splits:\n");
    for (node = nc_list; node; node = node->next)
    {
        Split *split = (Split *)node->data;
        gnc_numeric value = xaccSplitGetAmount (split);
        PINFO("  %s\n", gnc_numeric_to_string(value));
    }

    /* Run knapsack */
    /* Entries in the hash table are:
     *  - key   = amount to which we know how to clear (freed by GHashTable)
     *  - value = last split we used to clear this amount (not managed by GHashTable)
     */
    PINFO("Knapsacking ...\n");
    sack = g_hash_table_new_full (ght_gnc_numeric_hash, ght_gnc_numeric_equal, g_free, NULL);
    for (node = nc_list; node; node = node->next)
    {
        Split *split = (Split *)node->data;
        gnc_numeric split_value = xaccSplitGetAmount(split);

        GList *node;
        struct _sack_foreach_data_t data[1];
        data->split_value = split_value;
        data->reachable_list = 0;

        PINFO("  Split value: %s\n", gnc_numeric_to_string(split_value));

        /* For each value in the sack, compute a new reachable value */
        g_hash_table_foreach (sack, sack_foreach_func, data);

        /* Add the value of the split itself to the reachable_list */
        data->reachable_list = g_list_append(data->reachable_list, g_memdup(&split_value, sizeof(gnc_numeric)));

        /* Add everything to the sack, looking out for duplicates */
        for (node = data->reachable_list; node; node = node->next)
        {
            gnc_numeric *reachable_value = node->data;
            Split *toinsert_split = split;

            PINFO("    Reachable value: %s ", gnc_numeric_to_string(*reachable_value));

            /* Check if it already exists */
            if (g_hash_table_lookup_extended(sack, reachable_value, NULL, NULL))
            {
                /* If yes, we are in trouble, we reached an amount using two solutions */
                toinsert_split = NULL;
                PINFO("dup");
            }
            g_hash_table_insert (sack, reachable_value, toinsert_split);
            PINFO("\n");
        }
        g_list_free(data->reachable_list);
    }

    /* Check solution */
    PINFO("Rebuilding solution ...\n");
    while (!gnc_numeric_zero_p(toclear_value))
    {
        gpointer psplit = NULL;

        PINFO("  Left to clear: %s\n", gnc_numeric_to_string(toclear_value));
        if (g_hash_table_lookup_extended(sack, &toclear_value, NULL, &psplit))
        {
            if (psplit != NULL)
            {
                /* Cast the gpointer to the kind of pointer we actually need */
                Split *split = (Split *)psplit;
                toclear_list = g_list_prepend(toclear_list, split);
                toclear_value = gnc_numeric_sub_fixed(toclear_value,
                                                      xaccSplitGetAmount(split));
                PINFO("    Cleared: %s -> %s\n",
                      gnc_numeric_to_string(xaccSplitGetAmount(split)),
                      gnc_numeric_to_string(toclear_value));
            }
            else
            {
                /* We couldn't reconstruct the solution */
                PINFO("    Solution not unique.\n");
                gtk_label_set_text(data->status_label, _("Cannot uniquely clear splits. Found multiple possibilities."));
                return;
            }
        }
        else
        {
            PINFO("    No solution found.\n");
            gtk_label_set_text(data->status_label, _("The selected amount cannot be cleared."));
            return;
        }
    }
    g_hash_table_destroy (sack);

    /* Show solution */
    PINFO("Clearing splits:\n");
    for (node = toclear_list; node; node = node->next)
    {
        Split *split = node->data;
        char recn;
        gnc_numeric value;

        recn = xaccSplitGetReconcile (split);
        value = xaccSplitGetAmount (split);

        PINFO("  %c %s\n", recn, gnc_numeric_to_string(value));

        xaccSplitSetReconcile (split, CREC);
    }
    if (toclear_list == 0)
        PINFO("  None\n");

    /* Free lists */
    g_list_free(nc_list);
    g_list_free(toclear_list);

    /* Close window */
    gtk_widget_destroy(data->window);
    g_free(data);
}
Beispiel #17
0
gboolean
gnc_numeric_to_decimal(gnc_numeric *a, guint8 *max_decimal_places)
{
    guint8 decimal_places = 0;
    gnc_numeric converted_val;
    gint64 fraction;

    g_return_val_if_fail(a, FALSE);

    if (gnc_numeric_check(*a) != GNC_ERROR_OK)
        return FALSE;

    converted_val = *a;
    if (converted_val.denom <= 0)
    {
        converted_val = gnc_numeric_convert(converted_val, 1, GNC_HOW_DENOM_EXACT);
        if (gnc_numeric_check(converted_val) != GNC_ERROR_OK)
            return FALSE;
        *a = converted_val;
        if (max_decimal_places)
            *max_decimal_places = decimal_places;
        return TRUE;
    }

    /* Zero is easily converted. */
    if (converted_val.num == 0)
        converted_val.denom = 1;

    fraction = converted_val.denom;
    while (fraction != 1)
    {
        switch (fraction % 10)
        {
        case 0:
            fraction = fraction / 10;
            break;

        case 5:
            converted_val = gnc_numeric_mul(converted_val,
                                            gnc_numeric_create(2, 2),
                                            GNC_DENOM_AUTO,
                                            GNC_HOW_DENOM_EXACT |
                                            GNC_HOW_RND_NEVER);
            if (gnc_numeric_check(converted_val) != GNC_ERROR_OK)
                return FALSE;
            fraction = fraction / 5;
            break;

        case 2:
        case 4:
        case 6:
        case 8:
            converted_val = gnc_numeric_mul(converted_val,
                                            gnc_numeric_create(5, 5),
                                            GNC_DENOM_AUTO,
                                            GNC_HOW_DENOM_EXACT |
                                            GNC_HOW_RND_NEVER);
            if (gnc_numeric_check(converted_val) != GNC_ERROR_OK)
                return FALSE;
            fraction = fraction / 2;
            break;

        default:
            return FALSE;
        }

        decimal_places += 1;
    }

    if (max_decimal_places)
        *max_decimal_places = decimal_places;

    *a = converted_val;

    return TRUE;
}