예제 #1
0
/*
 * Create a payment of "amount" for the owner and match it with
 * the set of lots passed in. If not lots were given all open
 * lots for the owner are considered.
 */
void
gncOwnerApplyPayment (const GncOwner *owner, Transaction *txn, GList *lots,
                      Account *posted_acc, Account *xfer_acc,
                      gnc_numeric amount, gnc_numeric exch, Timespec date,
                      const char *memo, const char *num, gboolean auto_pay)
{
    GNCLot *payment_lot = NULL;
    GList *selected_lots = NULL;

    /* Verify our arguments */
    if (!owner || !posted_acc
               || (!xfer_acc && !gnc_numeric_zero_p (amount)) ) return;
    g_return_if_fail (owner->owner.undefined);

    /* If there's a real amount to transfer create a lot for this payment */
    if (!gnc_numeric_zero_p (amount))
        payment_lot = gncOwnerCreatePaymentLot (owner, txn, posted_acc, xfer_acc,
                                                amount, exch, date, memo, num);

    if (lots)
        selected_lots = lots;
    else if (auto_pay)
        selected_lots = xaccAccountFindOpenLots (posted_acc, gncOwnerLotMatchOwnerFunc,
                        (gpointer)owner, NULL);

    /* And link the selected lots and the payment lot together as well as possible.
     * If the payment was bigger than the selected documents/overpayments, only
     * part of the payment will be used. Similarly if more documents were selected
     * than the payment value set, not all documents will be marked as paid. */
    if (payment_lot)
        selected_lots = g_list_prepend (selected_lots, payment_lot);
    gncOwnerAutoApplyPaymentsWithLots (owner, selected_lots);
    g_list_free (selected_lots);
}
예제 #2
0
파일: Scrub.c 프로젝트: Bob-IT/gnucash
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;
}
예제 #3
0
파일: Scrub.c 프로젝트: Bob-IT/gnucash
void
xaccTransScrubImbalance (Transaction *trans, Account *root,
                         Account *account)
{
    gnc_numeric imbalance;

    if (!trans) return;

    ENTER ("()");

    /* Must look for orphan splits even if there is no imbalance. */
    xaccTransScrubSplits (trans);

    /* Return immediately if things are balanced. */
    if (xaccTransIsBalanced (trans))
    {
        LEAVE ("transaction is balanced");
        return;
    }

    if (! xaccTransUseTradingAccounts (trans))
    {
        gnc_transaction_balance_no_trading (trans, root, account);
        LEAVE ("transaction balanced, no trading accounts");
        return;
    }

    imbalance = gnc_transaction_adjust_trading_splits (trans, root);

    /* Balance the value, ignoring existing trading splits */
    if (! gnc_numeric_zero_p (imbalance))
    {
        PINFO ("Value unbalanced transaction");

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

    gnc_transaction_balance_trading (trans, root);
    if (gnc_numeric_zero_p(xaccTransGetImbalanceValue(trans)))
    {
        LEAVE ("()");
        return;
    }
    /* If the transaction is still not balanced, it's probably because there
       are splits with zero amount and non-zero value.  These are usually
       realized gain/loss splits.  Add a reversing split for each of them to
       balance the value. */

    gnc_transaction_balance_trading_more_splits (trans, root);
    if (!gnc_numeric_zero_p(xaccTransGetImbalanceValue(trans)))
        PERR("Balancing currencies unbalanced value");

}
예제 #4
0
파일: Scrub.c 프로젝트: Bob-IT/gnucash
/** Balance the transaction by adding more trading splits. This shouldn't
 * ordinarily be necessary.
 * @param trans the transaction to balance
 * @param root the root account
 */
static void
gnc_transaction_balance_trading_more_splits (Transaction *trans, Account *root)
{
    /* Copy the split list so we don't see the splits we're adding */
    GList *splits_dup = g_list_copy(trans->splits), *splits = NULL;
    const gnc_commodity  *txn_curr = xaccTransGetCurrency (trans);
    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;
            Split *balance_split;
            Account *account = NULL;

            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 */
                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(txn_curr),
                                         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);
}
예제 #5
0
static void gncOwnerOffsetLots (GNCLot *from_lot, GNCLot *to_lot, const GncOwner *owner)
{
    gnc_numeric target_offset;
    Split *split;

    /* from lot should not be a document lot because we're removing a split from there ! */
    if (gncInvoiceGetInvoiceFromLot (from_lot))
    {
        PWARN ("from_lot %p is a document lot. That is not allowed in gncOwnerOffsetLots", from_lot);
        return;
    }

    /* Get best matching split from from_lot to offset to_lot */
    target_offset = gnc_lot_get_balance (to_lot);
    if (gnc_numeric_zero_p (target_offset))
        return; // to_lot is already balanced, nothing more to do

    split = gncOwnerFindOffsettingSplit (from_lot, target_offset);
    if (!split)
        return; // No suitable offsetting split found, nothing more to do

    /* If the offsetting split is bigger than the amount needed to balance
     * to_lot, reduce the split so its reduced value closes to_lot exactly.
     * Note the negation in the reduction function. The split must be of
     * opposite sign of to_lot's balance in order to be able to close it.
     */
    if (gnc_numeric_compare (gnc_numeric_abs (xaccSplitGetValue (split)),
                             gnc_numeric_abs (target_offset)) > 0)
        gncOwnerReduceSplitTo (split, gnc_numeric_neg (target_offset));

    /* Move the reduced split from from_lot to to_lot */
    gnc_lot_add_split (to_lot, split);

}
예제 #6
0
/** This function tells the owner tree view whether or not to filter
 *  out a particular owner.  Owners may be filtered if the user
 *  has decided not to display inactive owners, or if the
 *  user has requested that owners with a zero total not be shown.
 *
 *  @param owner The owner that is being evaluated.
 *
 *  @param user_data A pointer to the OwnerFilterDialog struct.
 *
 *  @return TRUE if the owner should be visible.  FALSE if the
 *  owner should be hidden. */
gboolean
gnc_plugin_page_owner_tree_filter_owners (GncOwner *owner,
        gpointer user_data)
{
    OwnerFilterDialog *fd = user_data;
    gnc_numeric total;

    ENTER("owner %p:%s", owner, gncOwnerGetName(owner));

    if (!fd->show_inactive && !gncOwnerGetActive (owner))
    {
        LEAVE(" hide: inactive");
        return FALSE;
    }

    if (!fd->show_zero_total)
    {
        /* FIXME I'm not aware of any functions to get an owner's "balance" yet.
         *       This should be implemented before this function does anything useful.
         *       The code below is copied from the tree-view-account source to serve
         *       as an example.
        total = gncOwnerGetBalanceInCurrency (owner, NULL, TRUE);
        */
        total = gnc_numeric_zero();
        if (gnc_numeric_zero_p(total))
        {
            LEAVE(" hide: zero balance");
            return FALSE;
        }
    }

    return TRUE;
}
예제 #7
0
/** Verify whether we can save the entry, or warn the user when we can't
 * return TRUE if we can save, FALSE if there is a problem
 */
static gboolean
gnc_entry_ledger_verify_can_save (GncEntryLedger *ledger)
{
    gnc_numeric value;

    /* Compute the value and tax value of the current cursor */
    gnc_entry_ledger_compute_value (ledger, &value, NULL);

    /* If there is a value, make sure there is an account */
    if (! gnc_numeric_zero_p (value))
    {
        switch (ledger->type)
        {
        case GNCENTRY_INVOICE_ENTRY:
        case GNCENTRY_CUST_CREDIT_NOTE_ENTRY:
            if (!gnc_entry_ledger_verify_acc_cell_ok (ledger, ENTRY_IACCT_CELL,
                    _("This account should usually be of type income.")))
                return FALSE;
            break;
        case GNCENTRY_BILL_ENTRY:
        case GNCENTRY_EXPVOUCHER_ENTRY:
        case GNCENTRY_VEND_CREDIT_NOTE_ENTRY:
        case GNCENTRY_EMPL_CREDIT_NOTE_ENTRY:
            if (!gnc_entry_ledger_verify_acc_cell_ok (ledger, ENTRY_BACCT_CELL,
                    _("This account should usually be of type expense or asset.")))
                return FALSE;
            break;
        default:
            g_warning ("Unhandled ledger type");
            break;
        }
    }

    return TRUE;
}
예제 #8
0
/* Set the value for the given input amount */
void
gnc_tree_util_set_value_for_amount (GncTreeViewSplitReg *view, Transaction *trans, Split *split, gnc_numeric input)
{
    gnc_numeric  split_rate;
    gnc_numeric  amount;
    gnc_numeric  value, new_value;
    int denom;

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

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

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

    denom = gtu_sr_get_value_denom (split);

    split_rate = gnc_numeric_div (value, amount, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT);
    if (gnc_numeric_check (split_rate) != GNC_ERROR_OK)
        split_rate = gnc_numeric_create (100,100);

    new_value = gnc_numeric_mul (input, split_rate, denom, GNC_HOW_RND_ROUND_HALF_UP);

    xaccSplitSetValue (split, new_value);
    xaccSplitSetAmount (split, input);

    LEAVE("");
}
예제 #9
0
파일: Scrub2.c 프로젝트: Mechtilde/gnucash
gboolean
xaccScrubMergeSubSplits (Split *split, gboolean strict)
{
    gboolean rc = FALSE;
    Transaction *txn;
    SplitList *node;
    GNCLot *lot;

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

    txn = split->parent;

    // Don't mess with splits from an invoice transaction
    // Those are the responsibility of the business code
    if (gncInvoiceGetInvoiceFromTxn (txn)) return FALSE;

    lot = xaccSplitGetLot (split);

    ENTER ("(Lot=%s)", gnc_lot_get_title(lot));
restart:
    for (node = txn->splits; node; node = node->next)
    {
        Split *s = node->data;
        if (xaccSplitGetLot (s) != lot) continue;
        if (s == split) continue;
        if (qof_instance_get_destroying(s)) continue;

        // Don't mess with splits from an invoice transaction
        // Those are the responsibility of the business code
        if (gncInvoiceGetInvoiceFromTxn (s->parent)) return FALSE;

        if (strict)
        {
            /* OK, this split is in the same lot (and thus same account)
             * as the indicated split.  Make sure it is really a subsplit
             * of the split we started with.  It's possible to have two
             * splits in the same lot and transaction that are not subsplits
             * of each other, the test-period test suite does this, for
             * example.  Only worry about adjacent sub-splits.  By
             * repeatedly merging adjacent subsplits, we'll get the non-
             * adjacent ones too. */
            if (!xaccSplitIsPeerSplit (split, s))
                continue;
        }

        merge_splits (split, s);
        rc = TRUE;
        goto restart;
    }
    if (rc && gnc_numeric_zero_p (split->amount))
    {
        time64 pdate = xaccTransGetDate (txn);
        gchar *pdatestr = gnc_ctime (&pdate);
        PWARN ("Result of merge has zero amt!");
        PWARN ("Transaction details - posted date %s - description %s", pdatestr, xaccTransGetDescription(txn));
        g_free (pdatestr);
    }
    LEAVE (" splits merged=%d", rc);
    return rc;
}
gboolean
gnc_stock_split_assistant_details_complete (GtkAssistant *assistant,
        gpointer user_data)
{
    StockSplitInfo *info = user_data;
    gnc_numeric amount;
    gint result;

    result = gnc_amount_edit_expr_is_valid (GNC_AMOUNT_EDIT (info->distribution_edit), &amount, TRUE);
    if ( result != 0)
        return FALSE; /* Parsing error or field is empty */

    if (gnc_numeric_zero_p (amount))
        return FALSE; /* field value is 0 */

    result = gnc_amount_edit_expr_is_valid (GNC_AMOUNT_EDIT (info->price_edit), &amount, TRUE);
    if (result == -1)
        return TRUE; /* Optional field is empty */
    else if ( result > 0)
        return FALSE; /* Parsing error */
    else if (gnc_numeric_negative_p (amount))
        return FALSE; /* Negative price is not allowed */
    else
        return TRUE; /* Valid positive price */
}
예제 #11
0
void
xaccAccountAssignLots (Account *acc)
{
    SplitList *splits, *node;

    if (!acc) return;

    ENTER ("acc=%s", xaccAccountGetName(acc));
    xaccAccountBeginEdit (acc);

restart_loop:
    splits = xaccAccountGetSplitList(acc);
    for (node = splits; node; node = node->next)
    {
        Split * split = node->data;

        /* If already in lot, then no-op */
        if (split->lot) continue;

        /* Skip voided transactions */
        if (gnc_numeric_zero_p (split->amount) &&
                xaccTransGetVoidStatus(split->parent)) continue;

        if (xaccSplitAssign (split)) goto restart_loop;
    }
    xaccAccountCommitEdit (acc);
    LEAVE ("acc=%s", xaccAccountGetName(acc));
}
예제 #12
0
static const char *
gnc_price_cell_print_value (PriceCell *cell)
{
    if (cell->blank_zero && gnc_numeric_zero_p (cell->amount))
        return "";

    return xaccPrintAmount (cell->amount, cell->print_info);
}
static void
balance_cell_data_func (GtkTreeViewColumn *tree_column,
                        GtkCellRenderer *cell,
                        GtkTreeModel *model,
                        GtkTreeIter *iter,
                        gpointer user_data)
{
    Account *account;
    gnc_numeric balance;
    const gchar *string;
    GNCPrintAmountInfo print_info;
    hierarchy_data *data = (hierarchy_data *)user_data;
    gboolean allow_value;

    g_return_if_fail (GTK_TREE_MODEL (model));
    account = gnc_tree_view_account_get_account_from_iter (model, iter);

    balance = get_final_balance (data->balance_hash, account);
    if (gnc_numeric_zero_p (balance))
    {
        string = "";
    }
    else
    {
        print_info = gnc_account_print_info (account, FALSE);
        string = xaccPrintAmount (balance, print_info);
    }

    if (xaccAccountGetType(account) == ACCT_TYPE_EQUITY ||
            xaccAccountGetType(account) == ACCT_TYPE_TRADING)
    {
        allow_value = FALSE;
        string = _("zero");
    }
    else
    {
        GncAccountMergeDisposition disp;
        disp = determine_merge_disposition(gnc_book_get_root_account(gnc_get_current_book()), account);
        if (disp == GNC_ACCOUNT_MERGE_DISPOSITION_CREATE_NEW)
        {
            allow_value = !xaccAccountGetPlaceholder(account);
        }
        else
        {
            allow_value = FALSE;
            string = _("existing account");
        }
    }
    g_object_set (G_OBJECT (cell),
                  "text", string,
                  "editable", allow_value,
                  "sensitive", allow_value,
                  NULL);
}
static void
starting_balance_helper (Account *account, hierarchy_data *data)
{
    gnc_numeric balance;

    balance = get_final_balance (data->balance_hash, account);
    if (gnc_reverse_balance(account))
        balance = gnc_numeric_neg(balance);
    if (!gnc_numeric_zero_p (balance))
        gnc_account_create_opening_balance (account, balance, gnc_time (NULL),
                                            gnc_get_current_book ());
}
예제 #15
0
void
xaccLotFill (GNCLot *lot)
{
    Account *acc;
    Split *split;
    GNCPolicy *pcy;

    if (!lot) return;
    acc = gnc_lot_get_account(lot);
    pcy = gnc_account_get_policy(acc);

    ENTER ("(lot=%s, acc=%s)", gnc_lot_get_title(lot), xaccAccountGetName(acc));

    /* If balance already zero, we have nothing to do. */
    if (gnc_lot_is_closed (lot)) return;

    split = pcy->PolicyGetSplit (pcy, lot);
    if (!split) return;   /* Handle the common case */

    /* Reject voided transactions */
    if (gnc_numeric_zero_p(split->amount) &&
            xaccTransGetVoidStatus(split->parent)) return;

    xaccAccountBeginEdit (acc);

    /* Loop until we've filled up the lot, (i.e. till the
     * balance goes to zero) or there are no splits left.  */
    while (1)
    {
        Split *subsplit;

        subsplit = xaccSplitAssignToLot (split, lot);
        if (subsplit == split)
        {
            PERR ("Accounting Policy gave us a split that "
                  "doesn't fit into this lot\n"
                  "lot baln=%s, isclosed=%d, aplit amt=%s",
                  gnc_num_dbg_to_string (gnc_lot_get_balance(lot)),
                  gnc_lot_is_closed (lot),
                  gnc_num_dbg_to_string (split->amount));
            break;
        }

        if (gnc_lot_is_closed (lot)) break;

        split = pcy->PolicyGetSplit (pcy, lot);
        if (!split) break;
    }
    xaccAccountCommitEdit (acc);
    LEAVE ("(lot=%s, acc=%s)", gnc_lot_get_title(lot), xaccAccountGetName(acc));
}
예제 #16
0
파일: Scrub.c 프로젝트: Bob-IT/gnucash
/* Balance a transaction without trading accounts. */
static void
gnc_transaction_balance_no_trading (Transaction *trans, Account *root,
                                    Account *account)
{
    gnc_numeric imbalance  = xaccTransGetImbalanceValue (trans);

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

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

}
예제 #17
0
static const char * get_disc_entry (VirtualLocation virt_loc,
                                    gboolean translate,
                                    gboolean *conditionally_changed,
                                    gpointer user_data)
{
    GncEntryLedger *ledger = user_data;
    GncEntry *entry;
    gnc_numeric discount;

    entry = gnc_entry_ledger_get_entry (ledger, virt_loc.vcell_loc);
    discount = gncEntryGetInvDiscount (entry);
    if (gnc_numeric_zero_p (discount))
        return NULL;

    return xaccPrintAmount (discount, gnc_default_print_info (FALSE));
}
예제 #18
0
void
gnc_tree_util_split_reg_set_value_for (GncTreeViewSplitReg *view, Transaction *trans, Split *split, gnc_numeric input, gboolean force)
{
    GncTreeModelSplitReg *model;
    GtkWidget *window;
    Account *anchor;
    Account *acct = xaccSplitGetAccount (split);
    gnc_commodity *currency;
    gnc_numeric value, amount, rate;

    ENTER("set_value_for trans %p and split %p input %s force %d", trans, split, gnc_numeric_to_string (input), force);

    currency = xaccTransGetCurrency (trans);

    model = gnc_tree_view_split_reg_get_model_from_view (view);

    anchor = gnc_tree_model_split_reg_get_anchor (model);

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

    window = gnc_tree_view_split_reg_get_parent (view);

    if (gtu_sr_needs_exchange_rate (view, trans, split))
    {
        if (gtu_sr_handle_exchange_rate (view, input, trans, split, force))
        {
            ; //FIXME ??????
        }
        else
        {
            gnc_error_dialog (window, "%s",
                         _("Exchange Rate Canceled, using existing rate or default 1 to 1 rate if this is a new transaction."));
        }
        LEAVE("used exchange rate");
        return;
    }

    gnc_tree_util_split_reg_save_amount_values (view, trans, split, input);

    LEAVE(" ");
}
예제 #19
0
gboolean
xaccScrubMergeSubSplits (Split *split)
{
    gboolean rc = FALSE;
    Transaction *txn;
    SplitList *node;
    GNCLot *lot;
    const GncGUID *guid;

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

    txn = split->parent;
    lot = xaccSplitGetLot (split);

    ENTER ("(Lot=%s)", gnc_lot_get_title(lot));
restart:
    for (node = txn->splits; node; node = node->next)
    {
        Split *s = node->data;
        if (xaccSplitGetLot (s) != lot) continue;
        if (s == split) continue;
        if (qof_instance_get_destroying(s)) continue;

        /* OK, this split is in the same lot (and thus same account)
         * as the indicated split.  Make sure it is really a subsplit
         * of the split we started with.  It's possible to have two
         * splits in the same lot and transaction that are not subsplits
         * of each other, the test-period test suite does this, for
         * example.  Only worry about adjacent sub-splits.  By
         * repeatedly merging adjacent subsplits, we'll get the non-
         * adjacent ones too. */
        guid = qof_instance_get_guid(s);
        if (gnc_kvp_bag_find_by_guid (split->inst.kvp_data, "lot-split",
                                      "peer_guid", guid) == NULL)
            continue;

        merge_splits (split, s);
        rc = TRUE;
        goto restart;
    }
    if (gnc_numeric_zero_p (split->amount))
    {
        PWARN ("Result of merge has zero amt!");
    }
    LEAVE (" splits merged=%d", rc);
    return rc;
}
예제 #20
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;
}
예제 #21
0
static const char * get_qty_entry (VirtualLocation virt_loc,
                                   gboolean translate,
                                   gboolean *conditionally_changed,
                                   gpointer user_data)
{
    GncEntryLedger *ledger = user_data;
    GncEntry *entry;
    gnc_numeric qty;

    entry = gnc_entry_ledger_get_entry (ledger, virt_loc.vcell_loc);
    qty = gncEntryGetDocQuantity (entry, ledger->is_credit_note);

    if (gnc_numeric_zero_p (qty))
        return NULL;

    return xaccPrintAmount (qty, gnc_default_print_info (FALSE));
}
예제 #22
0
static Transaction*
gsr2_create_balancing_transaction (QofBook *book, Account *account,
                             time64 statement_date, gnc_numeric balancing_amount)
{
    Transaction *trans;
    Split *split;

    if (!account)
        return NULL;
    if (gnc_numeric_zero_p (balancing_amount))
        return NULL;

    xaccAccountBeginEdit (account);

    trans = xaccMallocTransaction (book);

    xaccTransBeginEdit (trans);

    // fill Transaction
    xaccTransSetCurrency (trans, gnc_account_or_default_currency (account, NULL));
    xaccTransSetDatePostedSecsNormalized (trans, statement_date);
    xaccTransSetDescription (trans, _("Balancing entry from reconciliation"));

    // 1. Split
    split = xaccMallocSplit (book);
    xaccTransAppendSplit (trans, split);
    xaccAccountInsertSplit  (account, split);
    xaccSplitSetAmount (split, balancing_amount);
    xaccSplitSetValue (split, balancing_amount);

    // 2. Split (no account is defined: split goes to orphan account)
    split = xaccMallocSplit (book);
    xaccTransAppendSplit (trans, split);

    balancing_amount = gnc_numeric_neg (balancing_amount);
    xaccSplitSetAmount (split, balancing_amount);
    xaccSplitSetValue (split, balancing_amount);

    xaccTransCommitEdit (trans);
    xaccAccountCommitEdit (account);
    return trans;
}
예제 #23
0
static const char * get_pric_entry (VirtualLocation virt_loc,
                                    gboolean translate,
                                    gboolean *conditionally_changed,
                                    gpointer user_data)
{
    GncEntryLedger *ledger = user_data;
    GncEntry *entry;
    gnc_numeric price;

    entry = gnc_entry_ledger_get_entry (ledger, virt_loc.vcell_loc);
    if (ledger->is_cust_doc)
        price = gncEntryGetInvPrice (entry);
    else
        price = gncEntryGetBillPrice (entry);

    if (gnc_numeric_zero_p (price))
        return NULL;

    return xaccPrintAmount (price, gnc_default_print_info (FALSE));
}
예제 #24
0
static void
fill_model (FindAccountDialog *facc_dialog, Account *account)
{
    GtkTreeModel *model;
    GtkTreeIter   iter;
    gchar        *fullname = gnc_account_get_full_name (account);
    gint          splits = xaccAccountCountSplits (account, TRUE);
    gnc_numeric   total = xaccAccountGetBalanceInCurrency (account, NULL, TRUE);

    PINFO("Add to Store: Account '%s'", fullname);

    model = gtk_tree_view_get_model (GTK_TREE_VIEW(facc_dialog->view));

    gtk_list_store_append (GTK_LIST_STORE(model), &iter);

    gtk_list_store_set (GTK_LIST_STORE(model), &iter,
                        ACC_FULL_NAME, fullname, ACCOUNT, account,
                        PLACE_HOLDER, (xaccAccountGetPlaceholder (account) == TRUE ? GTK_STOCK_YES : NULL),
                        HIDDEN, (xaccAccountGetHidden (account) == TRUE ? GTK_STOCK_YES : NULL),
                        NOT_USED, (splits == 0 ? GTK_STOCK_YES : NULL),
                        BAL_ZERO, (gnc_numeric_zero_p (total) == TRUE ? GTK_STOCK_YES : NULL), -1);
    g_free (fullname);
}
예제 #25
0
static const char * get_qty_entry (VirtualLocation virt_loc,
                                   gboolean translate,
                                   gboolean *conditionally_changed,
                                   gpointer user_data)
{
    GncEntryLedger *ledger = user_data;
    GncEntry *entry;
    gnc_numeric qty;

    entry = gnc_entry_ledger_get_entry (ledger, virt_loc.vcell_loc);
    qty = gncEntryGetQuantity (entry);

    if (gnc_numeric_zero_p (qty))
        return NULL;

    /* Credit notes have negative quantities, but the ledger should
     * display it as on the document, meaning positive.
     * So reverse the quantity for credit notes.
     */
    if (ledger->is_credit_note)
        qty = gnc_numeric_neg (qty);

    return xaccPrintAmount (qty, gnc_default_print_info (FALSE));
}
static int
fill_account_list (StockSplitInfo *info, Account *selected_account)
{
    GtkTreeRowReference *reference = NULL;
    GtkTreeView *view;
    GtkListStore *list;
    GtkTreeIter iter;
    GtkTreePath *path;
    GList *accounts;
    GList *node;
    gint rows = 0;
    gchar *full_name;

    view = GTK_TREE_VIEW(info->account_view);
    list = GTK_LIST_STORE(gtk_tree_view_get_model(view));

    gtk_list_store_clear (list);

    accounts = gnc_account_get_descendants_sorted (gnc_get_current_root_account ());
    for (node = accounts; node; node = node->next)
    {
        Account *account = node->data;
        GNCPrintAmountInfo print_info;
        const gnc_commodity *commodity;
        gnc_numeric balance;

        if (!xaccAccountIsPriced(account))
            continue;

        balance = xaccAccountGetBalance (account);
        if (gnc_numeric_zero_p (balance))
            continue;

        if (xaccAccountGetPlaceholder (account))
            continue;

        commodity = xaccAccountGetCommodity (account);

        full_name = gnc_account_get_full_name (account);
        print_info = gnc_account_print_info (account, FALSE);

        gtk_list_store_append(list, &iter);
        gtk_list_store_set(list, &iter,
                           SPLIT_COL_ACCOUNT,  account,
                           SPLIT_COL_FULLNAME, full_name,
                           SPLIT_COL_MNEMONIC, gnc_commodity_get_mnemonic(commodity),
                           SPLIT_COL_SHARES,   xaccPrintAmount(balance, print_info),
                           -1);

        if (account == selected_account)
        {
            path = gtk_tree_model_get_path(GTK_TREE_MODEL(list), &iter);
            reference = gtk_tree_row_reference_new(GTK_TREE_MODEL(list), path);
            gtk_tree_path_free(path);
        }

        g_free (full_name);

        rows++;
    }
    g_list_free(accounts);

    if (reference)
    {
        GtkTreeSelection* selection = gtk_tree_view_get_selection(view);
        path = gtk_tree_row_reference_get_path(reference);
        gtk_tree_row_reference_free(reference);
        if (path)
        {
            gtk_tree_selection_select_path(selection, path);
            gtk_tree_view_scroll_to_cell(view, path, NULL, TRUE, 0.5, 0.0);
            gtk_tree_path_free(path);
        }
    }

    return rows;
}
void
gnc_stock_split_assistant_finish (GtkAssistant *assistant,
                                  gpointer user_data)
{
    StockSplitInfo *info = user_data;
    GList *account_commits;
    GList *node;

    gnc_numeric amount;
    Transaction *trans;
    Account *account;
    Split *split;
    time64 date;

    account = info->acct;
    g_return_if_fail (account != NULL);

    amount = gnc_amount_edit_get_amount
             (GNC_AMOUNT_EDIT (info->distribution_edit));
    g_return_if_fail (!gnc_numeric_zero_p (amount));

    gnc_suspend_gui_refresh ();

    trans = xaccMallocTransaction (gnc_get_current_book ());

    xaccTransBeginEdit (trans);

    xaccTransSetCurrency (trans, gnc_default_currency ());

    date = gnc_date_edit_get_date (GNC_DATE_EDIT (info->date_edit));
    xaccTransSetDatePostedSecsNormalized (trans, date);

    {
        const char *description;

        description = gtk_entry_get_text (GTK_ENTRY (info->description_entry));
        xaccTransSetDescription (trans, description);
    }

    split = xaccMallocSplit (gnc_get_current_book ());

    xaccAccountBeginEdit (account);
    account_commits = g_list_prepend (NULL, account);

    xaccTransAppendSplit (trans, split);

    xaccAccountInsertSplit (account, split);

    xaccSplitSetAmount (split, amount);
    xaccSplitMakeStockSplit (split);
    /* Set split-action with gnc_set_num_action which is the same as
     * xaccSplitSetAction with these arguments */
    /* Translators: This string has a disambiguation prefix */
    gnc_set_num_action (NULL, split, NULL, Q_("Action Column|Split"));

    amount = gnc_amount_edit_get_amount (GNC_AMOUNT_EDIT (info->price_edit));
    if (gnc_numeric_positive_p (amount))
    {
        QofBook *book;
        GNCPrice *price;
        GNCPriceDB *pdb;
        GNCCurrencyEdit *ce;
        Timespec ts;

        ce = GNC_CURRENCY_EDIT (info->price_currency_edit);

        ts.tv_sec = date;
        ts.tv_nsec = 0;

        price = gnc_price_create (gnc_get_current_book ());

        gnc_price_begin_edit (price);
        gnc_price_set_commodity (price, xaccAccountGetCommodity (account));
        gnc_price_set_currency (price, gnc_currency_edit_get_currency (ce));
        gnc_price_set_time (price, ts);
        gnc_price_set_source (price, PRICE_SOURCE_STOCK_SPLIT);
        gnc_price_set_typestr (price, PRICE_TYPE_UNK);
        gnc_price_set_value (price, amount);
        gnc_price_commit_edit (price);

        book = gnc_get_current_book ();
        pdb = gnc_pricedb_get_db (book);

        if (!gnc_pricedb_add_price (pdb, price))
            gnc_error_dialog (info->window, "%s", _("Error adding price."));

    }

    amount = gnc_amount_edit_get_amount (GNC_AMOUNT_EDIT (info->cash_edit));
    if (gnc_numeric_positive_p (amount))
    {
        const char *memo;

        memo = gtk_entry_get_text (GTK_ENTRY (info->memo_entry));

        /* asset split */
        account = gnc_tree_view_account_get_selected_account (GNC_TREE_VIEW_ACCOUNT(info->asset_tree));

        split = xaccMallocSplit (gnc_get_current_book ());

        xaccAccountBeginEdit (account);
        account_commits = g_list_prepend (account_commits, account);

        xaccAccountInsertSplit (account, split);

        xaccTransAppendSplit (trans, split);

        xaccSplitSetAmount (split, amount);
        xaccSplitSetValue (split, amount);

        xaccSplitSetMemo (split, memo);


        /* income split */
        account = gnc_tree_view_account_get_selected_account (GNC_TREE_VIEW_ACCOUNT(info->income_tree));

        split = xaccMallocSplit (gnc_get_current_book ());

        xaccAccountBeginEdit (account);
        account_commits = g_list_prepend (account_commits, account);

        xaccAccountInsertSplit (account, split);

        xaccTransAppendSplit (trans, split);

        xaccSplitSetAmount (split, gnc_numeric_neg (amount));
        xaccSplitSetValue (split, gnc_numeric_neg (amount));

        xaccSplitSetMemo (split, memo);
    }

    xaccTransCommitEdit (trans);

    for (node = account_commits; node; node = node->next)
        xaccAccountCommitEdit (node->data);
    g_list_free (account_commits);

    gnc_resume_gui_refresh ();

    gnc_close_gui_component_by_data (ASSISTANT_STOCK_SPLIT_CM_CLASS, info);
}
예제 #28
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;
}
예제 #29
0
파일: policy.c 프로젝트: CAARNICL/gnucash
static Split *
DirectionPolicyGetSplit (GNCPolicy *pcy, GNCLot *lot, short reverse)
{
    Split *split;
    SplitList *node;
    gnc_commodity *common_currency;
    gboolean want_positive;
    gnc_numeric baln;
    Split *osplit;
    Transaction *otrans;
    Timespec open_ts;
    Account* lot_account;

    if (!pcy || !lot || !gnc_lot_get_split_list(lot)) return NULL;
    lot_account = gnc_lot_get_account(lot);
    if (!lot_account) return NULL;

    /* Recomputing the balance re-evaluates the lot closure */
    baln = gnc_lot_get_balance (lot);
    if (gnc_lot_is_closed(lot)) return NULL;

    want_positive = gnc_numeric_negative_p (baln);

    /* All splits in lot must share a common transaction currency. */
    split = gnc_lot_get_split_list(lot)->data;
    common_currency = split->parent->common_currency;

    /* Don't add a split to the lot unless it will be the new last
       split in the lot.  Otherwise our balance tests will be wrong
       and the lot may end up too thin or too fat. */
    osplit = gnc_lot_get_latest_split (lot);
    otrans = osplit ? xaccSplitGetParent (osplit) : 0;
    open_ts = xaccTransRetDatePostedTS (otrans);

    /* Walk over *all* splits in the account, till we find one that
     * hasn't been assigned to a lot.  Return that split.
     * Make use of the fact that the splits in an account are
     * already in date order; so we don't have to sort. */
    node = xaccAccountGetSplitList (lot_account);
    if (reverse)
    {
        node = g_list_last (node);
    }
    while (node)
    {
        gboolean is_match;
        gboolean is_positive;
        Timespec this_ts;
        split = node->data;
        if (split->lot) goto donext;

        /* Skip it if it's too early */
        this_ts = xaccTransRetDatePostedTS ( xaccSplitGetParent (split));
        if ((this_ts.tv_sec < open_ts.tv_sec) ||
                ((this_ts.tv_sec == open_ts.tv_sec) &&
                 (this_ts.tv_nsec < open_ts.tv_nsec)))
        {
            if (reverse)
                /* Going backwards, no point in looking further */
                break;
            goto donext;
        }

        /* Allow equiv currencies */
        is_match = gnc_commodity_equiv (common_currency,
                                        split->parent->common_currency);
        if (FALSE == is_match) goto donext;

        /* Disallow zero-amount splits in general. */
        if (gnc_numeric_zero_p(split->amount)) goto donext;

        is_positive = gnc_numeric_positive_p (split->amount);
        if ((want_positive && is_positive) ||
                ((!want_positive) && (!is_positive))) return split;
donext:
        if (reverse)
        {
            node = node->prev;
        }
        else
        {
            node = node->next;
        }
    }
    return NULL;
}
예제 #30
0
static void
gnc_split_register_save_cells (gpointer save_data,
                               gpointer user_data)
{
    SRSaveData *sd = save_data;
    SplitRegister *reg = user_data;
    Split *other_split;
    gnc_commodity *txn_cur;
    gnc_numeric rate = gnc_numeric_zero();

    g_return_if_fail (sd != NULL);

    if (!sd->do_scrub)
        return;

    other_split = xaccSplitGetOtherSplit (sd->split);
    txn_cur = xaccTransGetCurrency (sd->trans);

    xaccSplitScrub (sd->split);

    rate = gnc_split_register_get_rate_cell (reg, RATE_CELL);

    if (other_split && !sd->reg_expanded)
    {
        gnc_numeric amount, value = xaccSplitGetValue (sd->split);
        Account *acc;
        gboolean split_needs_amount;

        split_needs_amount = gnc_split_register_split_needs_amount(reg, sd->split);

        /* We are changing the rate on the current split, but it was not
         * handled in the debcred handler, so we need to do it here.
         */
        if (!sd->handled_dc && split_needs_amount && !gnc_numeric_zero_p (rate))
        {
            gnc_numeric amount = xaccSplitGetAmount (sd->split);
            value = gnc_numeric_div(
                        amount, rate, gnc_commodity_get_fraction(txn_cur), GNC_HOW_RND_ROUND_HALF_UP);
            xaccSplitSetValue (sd->split, value);

            /* XXX: do we need to set the amount on the other split? */
        }

        /* Now reverse the value for the other split */
        value = gnc_numeric_neg (value);

        if (gnc_split_register_split_needs_amount (reg, other_split))
        {
            acc = xaccSplitGetAccount (other_split);

            /* If we don't have an exchange rate then figure it out.  Or, if
             * BOTH splits require an amount, then most likely we're in the
             * strange case of having a transaction currency different than
             * _both_ accounts -- so grab the other exchange rate.
             */
            if (gnc_numeric_zero_p (rate) || split_needs_amount)
                rate = xaccTransGetAccountConvRate(xaccSplitGetParent (other_split),
                                                   acc);

            amount = gnc_numeric_mul (value, rate, xaccAccountGetCommoditySCU (acc),
                                      GNC_HOW_RND_ROUND_HALF_UP);
            xaccSplitSetAmount (other_split, amount);

        }

        xaccSplitSetValue (other_split, value);

        xaccSplitScrub (other_split);
    }
    else if (gnc_split_register_split_needs_amount (reg, sd->split) &&
             ! gnc_numeric_zero_p (rate))
    {
        /* this is either a multi-split or expanded transaction, so only
         * deal with this split...  In particular we need to reset the
         * Value if the conv-rate changed.
         *
         * If we handled the debcred then no need to do anything there --
         * the debcred handler did all the computation.  If NOT, then the
         * convrate changed -- reset the value from the amount.
         */
        if (!sd->handled_dc)
        {
            gnc_split_register_save_amount_values (sd, reg);
#if 0
            gnc_numeric value, amount;

            amount = xaccSplitGetAmount (sd->split);
            value = gnc_numeric_div (amount, rate, gnc_commodity_get_fraction (txn_cur),
                                     GNC_HOW_RND_ROUND_HALF_UP);
            xaccSplitSetValue (sd->split, value);
#endif
        }
    }
}