gnc_numeric
gnc_split_register_debcred_cell_value (SplitRegister *reg)
{
    PriceCell *cell;
    gnc_numeric new_amount;
    gnc_numeric credit;
    gnc_numeric debit;

    cell = (PriceCell *) gnc_table_layout_get_cell (reg->table->layout,
            CRED_CELL);
    credit = gnc_price_cell_get_value (cell);

    cell = (PriceCell *) gnc_table_layout_get_cell (reg->table->layout,
            DEBT_CELL);
    debit  = gnc_price_cell_get_value (cell);

    new_amount = gnc_numeric_sub_fixed (debit, credit);

    return new_amount;
}
示例#2
0
static gboolean
create_each_transaction_helper(Transaction *template_txn, void *user_data)
{
    Transaction *new_txn;
    GList *txn_splits, *template_splits;
    Split *copying_split;
    gnc_commodity *first_cmdty = NULL;
    gboolean err_flag = FALSE;
    SxTxnCreationData *creation_data;

    creation_data = (SxTxnCreationData*)user_data;

    /* FIXME: In general, this should [correctly] deal with errors such
       as not finding the approrpiate Accounts and not being able to
       parse the formula|credit/debit strings. */

    new_txn = xaccTransClone(template_txn);
    xaccTransBeginEdit(new_txn);

    g_debug("creating template txn desc [%s] for sx [%s]",
            xaccTransGetDescription(new_txn),
            xaccSchedXactionGetName(creation_data->instance->parent->sx));

    /* clear any copied KVP data */
    qof_instance_set_slots(QOF_INSTANCE(new_txn), kvp_frame_new());

    /* Bug#500427: copy the notes, if any */
    if (xaccTransGetNotes(template_txn) != NULL)
    {
        xaccTransSetNotes(new_txn, g_strdup(xaccTransGetNotes(template_txn)));
    }

    xaccTransSetDate(new_txn,
                     g_date_get_day(&creation_data->instance->date),
                     g_date_get_month(&creation_data->instance->date),
                     g_date_get_year(&creation_data->instance->date));

    /* the accounts and amounts are in the kvp_frames of the splits. */
    template_splits = xaccTransGetSplitList(template_txn);
    txn_splits = xaccTransGetSplitList(new_txn);
    if ((template_splits == NULL) || (txn_splits == NULL))
    {
        g_critical("transaction w/o splits for sx [%s]",
                   xaccSchedXactionGetName(creation_data->instance->parent->sx));
        xaccTransDestroy(new_txn);
        xaccTransCommitEdit(new_txn);
        return FALSE;
    }

    for (;
            txn_splits && template_splits;
            txn_splits = txn_splits->next, template_splits = template_splits->next)
    {
        Split *template_split;
        Account *split_acct;
        gnc_commodity *split_cmdty = NULL;

        /* FIXME: Ick.  This assumes that the split lists will be ordered
           identically. :( They are, but we'd rather not have to count on
           it. --jsled */
        template_split = (Split*)template_splits->data;
        copying_split = (Split*)txn_splits->data;

        if (!_get_template_split_account(creation_data->instance, template_split, &split_acct, creation_data->creation_errors))
        {
            err_flag = TRUE;
            break;
        }

        /* clear out any copied Split frame data. */
        qof_instance_set_slots(QOF_INSTANCE(copying_split), kvp_frame_new());

        split_cmdty = xaccAccountGetCommodity(split_acct);
        if (first_cmdty == NULL)
        {
            first_cmdty = split_cmdty;
            xaccTransSetCurrency(new_txn, first_cmdty);
        }

        xaccSplitSetAccount(copying_split, split_acct);

        {
            gnc_numeric credit_num, debit_num, final;
            gint gncn_error;

            credit_num = gnc_numeric_zero();
            debit_num = gnc_numeric_zero();

            _get_credit_formula_value(creation_data->instance, template_split, &credit_num, creation_data->creation_errors);
            _get_debit_formula_value(creation_data->instance, template_split, &debit_num, creation_data->creation_errors);

            final = gnc_numeric_sub_fixed( debit_num, credit_num );

            gncn_error = gnc_numeric_check(final);
            if (gncn_error != GNC_ERROR_OK)
            {
                GString *err = g_string_new("");
                g_string_printf(err, "error %d in SX [%s] final gnc_numeric value, using 0 instead",
                                gncn_error, xaccSchedXactionGetName(creation_data->instance->parent->sx));
                g_critical("%s", err->str);
                if (creation_data->creation_errors != NULL)
                    *creation_data->creation_errors = g_list_append(*creation_data->creation_errors, err);
                else
                    g_string_free(err, TRUE);
                final = gnc_numeric_zero();
            }
示例#3
0
void
xaccScrubSubSplitPrice (Split *split, int maxmult, int maxamtscu)
{
    gnc_numeric src_amt, src_val;
    SplitList *node;

    if (FALSE == is_subsplit (split)) return;

    ENTER (" ");
    /* Get 'price' of the indicated split */
    src_amt = xaccSplitGetAmount (split);
    src_val = xaccSplitGetValue (split);

    /* Loop over splits, adjust each so that it has the same
     * ratio (i.e. price).  Change the value to get things
     * right; do not change the amount */
    for (node = split->parent->splits; node; node = node->next)
    {
        Split *s = node->data;
        Transaction *txn = s->parent;
        gnc_numeric dst_amt, dst_val, target_val;
        gnc_numeric frac, delta;
        int scu;

        /* Skip the reference split */
        if (s == split) continue;

        scu = gnc_commodity_get_fraction (txn->common_currency);

        dst_amt = xaccSplitGetAmount (s);
        dst_val = xaccSplitGetValue (s);
        frac = gnc_numeric_div (dst_amt, src_amt,
                                GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE);
        target_val = gnc_numeric_mul (frac, src_val,
                                      scu, GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
        if (gnc_numeric_check (target_val))
        {
            PERR ("Numeric overflow of value\n"
                  "\tAcct=%s txn=%s\n"
                  "\tdst_amt=%s src_val=%s src_amt=%s\n",
                  xaccAccountGetName (s->acc),
                  xaccTransGetDescription(txn),
                  gnc_num_dbg_to_string(dst_amt),
                  gnc_num_dbg_to_string(src_val),
                  gnc_num_dbg_to_string(src_amt));
            continue;
        }

        /* If the required price changes are 'small', do nothing.
         * That is a case that the user will have to deal with
         * manually.  This routine is really intended only for
         * a gross level of synchronization.
         */
        delta = gnc_numeric_sub_fixed (target_val, dst_val);
        delta = gnc_numeric_abs (delta);
        if (maxmult * delta.num  < delta.denom) continue;

        /* If the amount is small, pass on that too */
        if ((-maxamtscu < dst_amt.num) && (dst_amt.num < maxamtscu)) continue;

        /* Make the actual adjustment */
        xaccTransBeginEdit (txn);
        xaccSplitSetValue (s, target_val);
        xaccTransCommitEdit (txn);
    }
    LEAVE (" ");
}
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);
}