コード例 #1
0
static void
gnc_template_register_save_xfrm_cell (BasicCell * cell,
                                      gpointer save_data,
                                      gpointer user_data)
{
    SRSaveData *sd = save_data;
    SplitRegister *reg = user_data;
    SRInfo *info = gnc_split_register_get_info (reg);
    Account *template_acc;
    const GncGUID *acctGUID;
    kvp_frame *kvpf;
    Account *acct;

    g_return_if_fail (gnc_basic_cell_has_name (cell, XFRM_CELL));

    /* save the account GncGUID into the kvp_data. */
    acct = gnc_split_register_get_account (reg, XFRM_CELL);
    if (!acct)
    {
        PERR ("unknown account");
        return;
    }

    acctGUID = xaccAccountGetGUID (acct);
    kvpf = xaccSplitGetSlots (sd->split);
    kvp_frame_set_slot_path (kvpf, kvp_value_new_guid(acctGUID),
                             GNC_SX_ID, GNC_SX_ACCOUNT, NULL);

    template_acc = xaccAccountLookup (&info->template_account,
                                      gnc_get_current_book ());

    /* set the actual account to the fake account for these templates */
    xaccAccountInsertSplit (template_acc, sd->split);
    qof_instance_set_dirty (QOF_INSTANCE (sd->split));
}
コード例 #2
0
ファイル: SchedXaction.c プロジェクト: goodvibes2/gnucash
static Split *
pack_split_info (TTSplitInfo *s_info, Account *parent_acct,
                 Transaction *parent_trans, QofBook *book)
{
    Split *split;
    const gchar *credit_formula;
    const gchar *debit_formula;
    const GncGUID *acc_guid;

    split = xaccMallocSplit(book);

    xaccSplitSetMemo(split,
                     gnc_ttsplitinfo_get_memo(s_info));

    /* Set split-action with gnc_set_num_action which is the same as
     * xaccSplitSetAction with these arguments */
    gnc_set_num_action(NULL, split, NULL,
                       gnc_ttsplitinfo_get_action(s_info));

    xaccAccountInsertSplit(parent_acct,
                           split);

    credit_formula = gnc_ttsplitinfo_get_credit_formula(s_info);
    debit_formula = gnc_ttsplitinfo_get_debit_formula(s_info);
    acc_guid = qof_entity_get_guid(QOF_INSTANCE(gnc_ttsplitinfo_get_account(s_info)));
    qof_instance_set (QOF_INSTANCE (split),
		      "sx-credit-formula", credit_formula,
		      "sx-debit-formula", debit_formula,
		      "sx-account", acc_guid,
		      NULL);

    return split;
}
コード例 #3
0
ファイル: gnc-transaction-xml-v2.c プロジェクト: 573/gnucash
static gboolean
spl_account_handler(xmlNodePtr node, gpointer data)
{
    struct split_pdata *pdata = data;
    GncGUID *id = dom_tree_to_guid(node);
    Account *account;

    g_return_val_if_fail(id, FALSE);

    account = xaccAccountLookup (id, pdata->book);
    if (!account && gnc_transaction_xml_v2_testing &&
            !guid_equal (id, guid_null ()))
    {
        account = xaccMallocAccount (pdata->book);
        xaccAccountSetGUID (account, id);
        xaccAccountSetCommoditySCU (account,
                                    xaccSplitGetAmount (pdata->split).denom);
    }

    xaccAccountInsertSplit (account, pdata->split);

    g_free(id);

    return TRUE;
}
コード例 #4
0
static void
gnc_split_register_save_mxfrm_cell (BasicCell * cell,
                                    gpointer save_data,
                                    gpointer user_data)
{
    SRSaveData *sd = save_data;
    SplitRegister *reg = user_data;
    Split * other_split;

    g_return_if_fail (gnc_basic_cell_has_name (cell, MXFRM_CELL));

    other_split = xaccSplitGetOtherSplit (sd->split);

    /* other_split may be null for two very different reasons:
     * (1) the parent transaction has three or more splits in it,
     *     and so the "other" split is ambiguous, and thus null.
     * (2) the parent transaction has only this one split as a child.
     *     and "other" is null because there is no other.
     *
     * In the case (2), we want to create the other split, so that
     * the user's request to transfer actually works out. */

    if (!other_split)
    {
        other_split = xaccTransGetSplit (sd->trans, 1);

        if (!other_split)
        {
            other_split = xaccMallocSplit (gnc_get_current_book ());
            xaccTransAppendSplit (sd->trans, other_split);
        }
    }

    if (other_split)
    {
        Account *old_acc;
        Account *new_acc;

        /* Do some reparenting. Insertion into new account
         * will automatically delete from the old account. */
        old_acc = xaccSplitGetAccount (other_split);
        new_acc = gnc_split_register_get_account (reg, MXFRM_CELL);

        if ((new_acc != NULL) && (old_acc != new_acc))
            xaccAccountInsertSplit (new_acc, other_split);
    }
}
コード例 #5
0
/* OK, the handling of transfers gets complicated because it depends
 * on what was displayed to the user. For a multi-line display, we
 * just reparent the indicated split. For a two-line display, we want
 * to reparent the "other" split, but only if there is one. XFRM is
 * the straight split, MXFRM is the mirrored split. */
static void
gnc_split_register_save_xfrm_cell (BasicCell * cell,
                                   gpointer save_data,
                                   gpointer user_data)
{
    SRSaveData *sd = save_data;
    SplitRegister *reg = user_data;
    Account *old_acc;
    Account *new_acc;

    g_return_if_fail (gnc_basic_cell_has_name (cell, XFRM_CELL));

    old_acc = xaccSplitGetAccount (sd->split);

    new_acc = gnc_split_register_get_account (reg, XFRM_CELL);

    if ((new_acc != NULL) && (old_acc != new_acc))
        xaccAccountInsertSplit (new_acc, sd->split);
}
コード例 #6
0
ファイル: gnc-split-reg2.c プロジェクト: Gnucash/gnucash
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;
}
コード例 #7
0
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);
}
コード例 #8
0
static void
test_transaction (void)
{
    int i;

    for (i = 0; i < 50; i++)
    {
        Transaction* ran_trn;
        xmlNodePtr test_node;
        gnc_commodity* com, *new_com;
        gchar* filename1;
        int fd;

        /* The next line exists for its side effect of creating the
         * account tree. */
        get_random_account_tree (book);
        ran_trn = get_random_transaction (book);
        new_com = get_random_commodity (book);
        if (!ran_trn)
        {
            failure_args ("transaction_xml", __FILE__, __LINE__,
                          "get_random_transaction returned NULL");
            return;
        }

        {
            /* xaccAccountInsertSplit can reorder the splits. */
            GList* list = g_list_copy (xaccTransGetSplitList (ran_trn));
            GList* node = list;
            for (; node; node = node->next)
            {
                Split* s = static_cast<decltype (s)> (node->data);
                Account* a = xaccMallocAccount (book);

                xaccAccountBeginEdit (a);
                xaccAccountSetCommodity (a, new_com);
                xaccAccountSetCommoditySCU (a, xaccSplitGetAmount (s).denom);
                xaccAccountInsertSplit (a, s);
                xaccAccountCommitEdit (a);
            }
            g_list_free (list);
        }

        com = xaccTransGetCurrency (ran_trn);

        test_node = gnc_transaction_dom_tree_create (ran_trn);
        if (!test_node)
        {
            failure_args ("transaction_xml", __FILE__, __LINE__,
                          "gnc_transaction_dom_tree_create returned NULL");
            really_get_rid_of_transaction (ran_trn);
            continue;
        }
        auto compare_msg = node_and_transaction_equal (test_node, ran_trn);
        if (compare_msg != nullptr)
        {
            failure_args ("transaction_xml", __FILE__, __LINE__,
                          "node and transaction were not equal: %s",
                          compare_msg);
            xmlElemDump (stdout, NULL, test_node);
            printf ("\n");
            fflush (stdout);
            xmlFreeNode (test_node);
            really_get_rid_of_transaction (ran_trn);
            continue;
        }
        else
        {
            success_args ("transaction_xml", __FILE__, __LINE__, "%d", i);
        }

        filename1 = g_strdup_printf ("test_file_XXXXXX");

        fd = g_mkstemp (filename1);

        write_dom_node_to_file (test_node, fd);

        close (fd);

        {
            GList* node = xaccTransGetSplitList (ran_trn);
            for (; node; node = node->next)
            {
                Split* s = static_cast<decltype (s)> (node->data);
                Account* a1 = xaccSplitGetAccount (s);
                Account* a2 = xaccMallocAccount (book);

                xaccAccountBeginEdit (a2);
                xaccAccountSetCommoditySCU (a2, xaccAccountGetCommoditySCU (a1));
                xaccAccountSetGUID (a2, xaccAccountGetGUID (a1));
                xaccAccountCommitEdit (a2);
            }
        }

        {
            sixtp* parser;
            tran_data data;

            const char* msg =
                "[xaccAccountScrubCommodity()] Account \"\" does not have a commodity!";
            const char* logdomain = "gnc.engine.scrub";
            GLogLevelFlags loglevel = static_cast<decltype (loglevel)>
                                      (G_LOG_LEVEL_CRITICAL);
            TestErrorStruct check = { loglevel, const_cast<char*> (logdomain),
                                      const_cast<char*> (msg)
                                    };
            g_log_set_handler (logdomain, loglevel,
                               (GLogFunc)test_checked_handler, &check);
            data.trn = ran_trn;
            data.com = com;
            data.value = i;
            parser = gnc_transaction_sixtp_parser_create ();

            if (!gnc_xml_parse_file (parser, filename1, test_add_transaction,
                                     (gpointer)&data, book))
            {
                failure_args ("gnc_xml_parse_file returned FALSE",
                              __FILE__, __LINE__, "%d", i);
            }
            else
                really_get_rid_of_transaction (data.new_trn);
        }
        /* no handling of circular data structures.  We'll do that later */
        /* sixtp_destroy(parser); */


        g_unlink (filename1);
        g_free (filename1);
        really_get_rid_of_transaction (ran_trn);
        xmlFreeNode (test_node);
    }
}
コード例 #9
0
ファイル: gncOwner.c プロジェクト: Justinadanielson/gnucash
void gncOwnerAutoApplyPaymentsWithLots (const GncOwner *owner, GList *lots)
{
    GList *base_iter;

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

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

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

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

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

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

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


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

            GNCLot *balancing_lot = lot_iter->data;

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

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

            payment_lot_balance = gnc_lot_get_balance (balancing_lot);

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

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

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

                xaccAccountBeginEdit (acct);

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

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

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

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

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


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

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

            xaccTransCommitEdit (txn);
            xaccAccountCommitEdit (acct);

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

        }

    }
}
コード例 #10
0
ファイル: gncOwner.c プロジェクト: Justinadanielson/gnucash
GNCLot *
gncOwnerCreatePaymentLot (const GncOwner *owner, Transaction *txn,
                          Account *posted_acc, Account *xfer_acc,
                          gnc_numeric amount, gnc_numeric exch, Timespec date,
                          const char *memo, const char *num)
{
    QofBook *book;
    Split *split;
    const char *name;
    gnc_commodity *commodity;
    Split *xfer_split = NULL;
    GNCLot *payment_lot;

    /* Verify our arguments */
    if (!owner || !posted_acc || !xfer_acc) return NULL;
    g_return_val_if_fail (owner->owner.undefined != NULL, NULL);

    /* Compute the ancillary data */
    book = gnc_account_get_book (posted_acc);
    name = gncOwnerGetName (gncOwnerGetEndOwner ((GncOwner*)owner));
    commodity = gncOwnerGetCurrency (owner);
//    reverse = use_reversed_payment_amounts(owner);

    if (txn)
    {
        /* Pre-existing transaction was specified. We completely clear it,
         * except for the split in the transfer account, unless the
         * transaction can't be reused (wrong currency, wrong transfer account).
         * In that case, the transaction is simply removed and an new
         * one created. */

        xfer_split = xaccTransFindSplitByAccount(txn, xfer_acc);

        if (xaccTransGetCurrency(txn) != gncOwnerGetCurrency (owner))
        {
            g_message("Uh oh, mismatching currency/commodity between selected transaction and owner. We fall back to manual creation of a new transaction.");
            xfer_split = NULL;
        }

        if (!xfer_split)
        {
            g_message("Huh? Asset account not found anymore. Fully deleting old txn and now creating a new one.");

            xaccTransBeginEdit (txn);
            xaccTransDestroy (txn);
            xaccTransCommitEdit (txn);

            txn = NULL;
        }
        else
        {
            int i = 0;
            xaccTransBeginEdit (txn);
            while (i < xaccTransCountSplits(txn))
            {
                Split *split = xaccTransGetSplit (txn, i);
                if (split == xfer_split)
                {
                    gnc_set_num_action (NULL, split, num, _("Payment"));
                    ++i;
                }
                else
                {
                    xaccSplitDestroy(split);
                }
            }
            /* Note: don't commit transaction now - that would insert an imbalance split.*/
        }
    }

    /* Create the transaction if we don't have one yet */
    if (!txn)
    {
        txn = xaccMallocTransaction (book);
        xaccTransBeginEdit (txn);
    }

    /* Insert a split for the transfer account if we don't have one yet */
    if (!xfer_split)
    {

        /* Set up the transaction */
        xaccTransSetDescription (txn, name ? name : "");
        /* set per book option */
        xaccTransSetCurrency (txn, commodity);
        xaccTransSetDateEnteredSecs (txn, gnc_time (NULL));
        xaccTransSetDatePostedTS (txn, &date);


        /* The split for the transfer account */
        split = xaccMallocSplit (book);
        xaccSplitSetMemo (split, memo);
        /* set per book option */
        gnc_set_num_action (NULL, split, num, _("Payment"));
        xaccAccountBeginEdit (xfer_acc);
        xaccAccountInsertSplit (xfer_acc, split);
        xaccAccountCommitEdit (xfer_acc);
        xaccTransAppendSplit (txn, split);

        if (gnc_commodity_equal(xaccAccountGetCommodity(xfer_acc), commodity))
        {
            xaccSplitSetBaseValue (split, amount, commodity);
        }
        else
        {
            /* Need to value the payment in terms of the owner commodity */
            gnc_numeric payment_value = gnc_numeric_mul(amount,
                                        exch, GNC_DENOM_AUTO, GNC_HOW_RND_ROUND_HALF_UP);

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

    /* Add a split in the post account */
    split = xaccMallocSplit (book);
    xaccSplitSetMemo (split, memo);
    /* set per book option */
    gnc_set_num_action (NULL, split, num, _("Payment"));
    xaccAccountBeginEdit (posted_acc);
    xaccAccountInsertSplit (posted_acc, split);
    xaccAccountCommitEdit (posted_acc);
    xaccTransAppendSplit (txn, split);
    xaccSplitSetBaseValue (split, gnc_numeric_neg (amount), commodity);

    /* Create a new lot for the payment */
    payment_lot = gnc_lot_new (book);
    gncOwnerAttachToLot (owner, payment_lot);
    gnc_lot_add_split (payment_lot, split);

    /* Mark the transaction as a payment */
    gnc_set_num_action (txn, NULL, num, _("Payment"));
    xaccTransSetTxnType (txn, TXN_TYPE_PAYMENT);

    /* Commit this new transaction */
    xaccTransCommitEdit (txn);

    return payment_lot;
}
コード例 #11
0
static void
test_transaction(void)
{
    int i;

    for (i = 0; i < 50; i++)
    {
        Transaction *ran_trn;
        Account *root;
        xmlNodePtr test_node;
        gnc_commodity *com, *new_com;
        gchar *compare_msg;
        gchar *filename1;
        int fd;

        /* The next line exists for its side effect of creating the
         * account tree. */
        root = get_random_account_tree(book);
        ran_trn = get_random_transaction(book);
        new_com = get_random_commodity( book );
        if (!ran_trn)
        {
            failure_args("transaction_xml", __FILE__, __LINE__,
                         "get_random_transaction returned NULL");
            return;
        }

        {
            /* xaccAccountInsertSplit can reorder the splits. */
            GList * list = g_list_copy(xaccTransGetSplitList (ran_trn));
            GList * node = list;
            for ( ; node; node = node->next)
            {
                Split * s = node->data;
                Account * a = xaccMallocAccount(book);

                xaccAccountBeginEdit (a);
                xaccAccountSetCommodity( a, new_com );
                xaccAccountSetCommoditySCU (a, xaccSplitGetAmount (s).denom);
                xaccAccountInsertSplit (a, s);
                xaccAccountCommitEdit (a);
            }
            g_list_free(list);
        }

        com = xaccTransGetCurrency (ran_trn);

        test_node = gnc_transaction_dom_tree_create(ran_trn);
        if (!test_node)
        {
            failure_args("transaction_xml", __FILE__, __LINE__,
                         "gnc_transaction_dom_tree_create returned NULL");
            really_get_rid_of_transaction(ran_trn);
            continue;
        }

        if ((compare_msg = node_and_transaction_equal(test_node, ran_trn)) !=
                NULL)
        {
            failure_args("transaction_xml", __FILE__, __LINE__,
                         "node and transaction were not equal: %s",
                         compare_msg);
            xmlElemDump(stdout, NULL, test_node);
            printf("\n");
            fflush(stdout);
            xmlFreeNode(test_node);
            really_get_rid_of_transaction(ran_trn);
            continue;
        }
        else
        {
            success_args("transaction_xml", __FILE__, __LINE__, "%d", i );
        }

        filename1 = g_strdup_printf("test_file_XXXXXX");

        fd = g_mkstemp(filename1);

        write_dom_node_to_file(test_node, fd);

        close(fd);

        {
            GList * node = xaccTransGetSplitList (ran_trn);
            for ( ; node; node = node->next)
            {
                Split * s = node->data;
                Account * a1 = xaccSplitGetAccount(s);
                Account * a2 = xaccMallocAccount(book);

                xaccAccountBeginEdit (a2);
                xaccAccountSetCommoditySCU (a2, xaccAccountGetCommoditySCU (a1));
                xaccAccountSetGUID (a2, xaccAccountGetGUID (a1));
                xaccAccountCommitEdit (a2);
            }
        }

        {
            sixtp *parser;
            tran_data data;

            data.trn = ran_trn;
            data.com = com;
            data.value = i;

            g_print(" There will follow a bunch of CRIT scrub errors about the account not having a commodity. There isn't an account in the XML, so of course not. Ignore the errors\n");
            parser = gnc_transaction_sixtp_parser_create();

            if (!gnc_xml_parse_file(parser, filename1, test_add_transaction,
                                    (gpointer)&data, book))
            {
                failure_args("gnc_xml_parse_file returned FALSE",
                             __FILE__, __LINE__, "%d", i);
            }
            else
                really_get_rid_of_transaction (data.new_trn);

            /* no handling of circular data structures.  We'll do that later */
            /* sixtp_destroy(parser); */
        }

        g_unlink(filename1);
        g_free(filename1);
        really_get_rid_of_transaction(ran_trn);
        xmlFreeNode(test_node);
    }
}
コード例 #12
0
ファイル: gncOwner.c プロジェクト: ShawnMcGough/gnucash
static void
gncOwnerCreateLotLink (GNCLot *from_lot, GNCLot *to_lot, const GncOwner *owner)
{
    const gchar *action = _("Lot Link");
    Account *acct = gnc_lot_get_account (from_lot);
    const gchar *name = gncOwnerGetName (gncOwnerGetEndOwner (owner));
    Transaction *ll_txn = NULL;
    gnc_numeric from_lot_bal, to_lot_bal;
    Timespec from_ts, to_ts;
    time64 time_posted;
    Split *split;

    /* Sanity check */
    if (!gncInvoiceGetInvoiceFromLot (from_lot) ||
        !gncInvoiceGetInvoiceFromLot (to_lot))
        return;

    /* Determine transaction date based on lot splits */
    from_ts = xaccTransRetDatePostedTS (xaccSplitGetParent (gnc_lot_get_latest_split (from_lot)));
    to_ts   = xaccTransRetDatePostedTS (xaccSplitGetParent (gnc_lot_get_latest_split (to_lot)));
    if (timespecToTime64 (from_ts) >= timespecToTime64 (to_ts))
        time_posted = timespecToTime64 (from_ts);
    else
        time_posted = timespecToTime64 (to_ts);

    /* Figure out how much we can offset between the lots */
    from_lot_bal = gnc_lot_get_balance (from_lot);
    to_lot_bal = gnc_lot_get_balance (to_lot);
    if (gnc_numeric_compare (gnc_numeric_abs (from_lot_bal),
                             gnc_numeric_abs (to_lot_bal)) > 0)
        from_lot_bal = gnc_numeric_neg (to_lot_bal);
    else
        to_lot_bal = gnc_numeric_neg (from_lot_bal);

    xaccAccountBeginEdit (acct);

    /* Look for a pre-existing lot link we can extend */
    ll_txn = get_ll_transaction_from_lot (from_lot);

    if (!ll_txn)
        ll_txn = get_ll_transaction_from_lot (to_lot);

    if (!ll_txn)
    {
        /* No pre-existing lot link. Create one. */
        Timespec ts;

        timespecFromTime64 (&ts, time_posted);

        ll_txn = xaccMallocTransaction (gnc_lot_get_book (from_lot));
        xaccTransBeginEdit (ll_txn);

        xaccTransSetDescription (ll_txn, name ? name : "(Unknown)");
        xaccTransSetCurrency (ll_txn, xaccAccountGetCommodity(acct));
        xaccTransSetDateEnteredSecs (ll_txn, gnc_time (NULL));
        xaccTransSetDatePostedTS (ll_txn, &ts);
        xaccTransSetTxnType (ll_txn, TXN_TYPE_LINK);
    }
    else
    {
        Timespec ts = xaccTransRetDatePostedTS (ll_txn);
        xaccTransBeginEdit (ll_txn);

        /* Maybe we need to update the post date of the transaction ? */
        if (time_posted > timespecToTime64 (ts))
        {
            timespecFromTime64 (&ts, time_posted);
            xaccTransSetDatePostedTS (ll_txn, &ts);

        }
    }

    /* Create a split for the from_lot */
    split = xaccMallocSplit (gnc_lot_get_book (from_lot));
    /* set Action using utility function */
    gnc_set_num_action (NULL, split, NULL, action);
    xaccAccountInsertSplit (acct, split);
    xaccTransAppendSplit (ll_txn, split);
    /* To offset the lot balance, the split must be of the opposite sign */
    xaccSplitSetBaseValue (split, gnc_numeric_neg (from_lot_bal), xaccAccountGetCommodity(acct));
    gnc_lot_add_split (from_lot, split);

    /* Create a split for the to_lot */
    split = xaccMallocSplit (gnc_lot_get_book (to_lot));
    /* set Action using utility function */
    gnc_set_num_action (NULL, split, NULL, action);
    xaccAccountInsertSplit (acct, split);
    xaccTransAppendSplit (ll_txn, split);
    /* To offset the lot balance, the split must be of the opposite sign */
    xaccSplitSetBaseValue (split, gnc_numeric_neg (to_lot_bal), xaccAccountGetCommodity(acct));
    gnc_lot_add_split (to_lot, split);

    xaccTransCommitEdit (ll_txn);


    /* Do some post-cleaning on the lots
     * The above actions may have created splits that are
     * in the same transaction and lot. These can be merged.
     */
    xaccScrubMergeLotSubSplits (to_lot, FALSE);
    xaccScrubMergeLotSubSplits (from_lot, FALSE);
    /* And finally set the same memo for all remaining splits
     * It's a convenience for the users to identify all documents
     * involved in the link.
     */
    gncOwnerSetLotLinkMemo (ll_txn);
    xaccAccountCommitEdit (acct);
}
コード例 #13
0
/* File pointer must already be at the begining of a record */
static void  process_trans_record(  FILE *log_file)
{
    char read_buf[2048];
    char *read_retval;
    char * trans_ro = NULL;
    const char * record_end_str = "===== END";
    int first_record = TRUE;
    int record_ended = FALSE;
    int split_num = 0;
    split_record record;
    Transaction * trans = NULL;
    Split * split = NULL;
    Account * acct = NULL;
    QofBook * book = gnc_get_current_book();

    DEBUG("process_trans_record(): Begin...\n");

    while ( record_ended == FALSE)
    {
        read_retval = fgets(read_buf, sizeof(read_buf), log_file);
        if (read_retval != NULL && strncmp(record_end_str, read_buf, strlen(record_end_str)) != 0) /* If we are not at the end of the record */
        {
            split_num++;
            /*DEBUG("process_trans_record(): Line read: %s%s",read_buf ,"\n");*/
            record = interpret_split_record( read_buf);
            dump_split_record( record);
            if (record.log_action_present)
            {
                switch (record.log_action)
                {
                case split_record::LOG_BEGIN_EDIT:
                    DEBUG("process_trans_record():Ignoring log action: LOG_BEGIN_EDIT"); /*Do nothing, there is no point*/
                    break;
                case split_record::LOG_ROLLBACK:
                    DEBUG("process_trans_record():Ignoring log action: LOG_ROLLBACK");/*Do nothing, since we didn't do the begin_edit either*/
                    break;
                case split_record::LOG_DELETE:
                    DEBUG("process_trans_record(): Playing back LOG_DELETE");
                    if ((trans = xaccTransLookup (&(record.trans_guid), book)) != NULL
                            && first_record == TRUE)
                    {
                        first_record = FALSE;
                        if (xaccTransGetReadOnly(trans))
                        {
                            PWARN("Destroying a read only transaction.");
                            xaccTransClearReadOnly(trans);
                        }
                        xaccTransBeginEdit(trans);
                        xaccTransDestroy(trans);
                    }
                    else if (first_record == TRUE)
                    {
                        PERR("The transaction to delete was not found!");
                    }
                    else
                        xaccTransDestroy(trans);
                    break;
                case split_record::LOG_COMMIT:
                    DEBUG("process_trans_record(): Playing back LOG_COMMIT");
                    if (record.trans_guid_present == TRUE
                            && first_record == TRUE)
                    {
                        trans = xaccTransLookupDirect (record.trans_guid, book);
                        if (trans != NULL)
                        {
                            DEBUG("process_trans_record(): Transaction to be edited was found");
                            xaccTransBeginEdit(trans);
                            trans_ro = g_strdup(xaccTransGetReadOnly(trans));
                            if (trans_ro)
                            {
                                PWARN("Replaying a read only transaction.");
                                xaccTransClearReadOnly(trans);
                            }
                        }
                        else
                        {
                            DEBUG("process_trans_record(): Creating a new transaction");
                            trans = xaccMallocTransaction (book);
                            xaccTransBeginEdit(trans);
                        }

                        xaccTransSetGUID (trans, &(record.trans_guid));
                        /*Fill the transaction info*/
                        if (record.date_entered_present)
                        {
                            xaccTransSetDateEnteredTS(trans, &(record.date_entered));
                        }
                        if (record.date_posted_present)
                        {
                            xaccTransSetDatePostedTS(trans, &(record.date_posted));
                        }
                        if (record.trans_num_present)
                        {
                            xaccTransSetNum(trans, record.trans_num);
                        }
                        if (record.trans_descr_present)
                        {
                            xaccTransSetDescription(trans, record.trans_descr);
                        }
                        if (record.trans_notes_present)
                        {
                            xaccTransSetNotes(trans, record.trans_notes);
                        }
                    }
                    if (record.split_guid_present == TRUE) /*Fill the split info*/
                    {
                        gboolean is_new_split;

                        split = xaccSplitLookupDirect (record.split_guid, book);
                        if (split != NULL)
                        {
                            DEBUG("process_trans_record(): Split to be edited was found");
                            is_new_split = FALSE;
                        }
                        else
                        {
                            DEBUG("process_trans_record(): Creating a new split");
                            split = xaccMallocSplit(book);
                            is_new_split = TRUE;
                        }
                        xaccSplitSetGUID (split, &(record.split_guid));
                        if (record.acc_guid_present)
                        {
                            acct = xaccAccountLookupDirect(record.acc_guid, book);
                            xaccAccountInsertSplit(acct, split);
                        }
                        if (is_new_split)
                            xaccTransAppendSplit(trans, split);

                        if (record.split_memo_present)
                        {
                            xaccSplitSetMemo(split, record.split_memo);
                        }
                        if (record.split_action_present)
                        {
                            xaccSplitSetAction(split, record.split_action);
                        }
                        if (record.date_reconciled_present)
                        {
                            xaccSplitSetDateReconciledTS (split, &(record.date_reconciled));
                        }
                        if (record.split_reconcile_present)
                        {
                            xaccSplitSetReconcile(split, record.split_reconcile);
                        }

                        if (record.amount_present)
                        {
                            xaccSplitSetAmount(split, record.amount);
                        }
                        if (record.value_present)
                        {
                            xaccSplitSetValue(split, record.value);
                        }
                    }
                    first_record = FALSE;
                    break;
                }
            }
            else
            {
                PERR("Corrupted record");
            }
        }
        else /* The record ended */
        {
            record_ended = TRUE;
            DEBUG("process_trans_record(): Record ended\n");
            if (trans != NULL) /*If we played with a transaction, commit it here*/
            {
                xaccTransScrubCurrencyFromSplits(trans);
                xaccTransSetReadOnly(trans, trans_ro);
                xaccTransCommitEdit(trans);
                g_free(trans_ro);
            }
        }
    }
}
コード例 #14
0
ファイル: gnc-ofx-import.c プロジェクト: rratliff/gnucash
int ofx_proc_transaction_cb(struct OfxTransactionData data, void * transaction_user_data)
{
    char dest_string[255];
    time64 current_time = gnc_time (NULL);
    Account *account;
    Account *investment_account = NULL;
    Account *income_account = NULL;
    gchar *investment_account_text, *investment_account_onlineid;
    gnc_commodity *currency = NULL;
    gnc_commodity *investment_commodity = NULL;
    gnc_numeric gnc_amount, gnc_units;
    QofBook *book;
    Transaction *transaction;
    Split *split;
    gchar *notes, *tmp;

    g_assert(gnc_ofx_importer_gui);

    if (!data.account_id_valid)
    {
        PERR("account ID for this transaction is unavailable!");
        return 0;
    }

    account = gnc_import_select_account(gnc_gen_trans_list_widget(gnc_ofx_importer_gui),
                                        data.account_id, 0, NULL, NULL,
                                        ACCT_TYPE_NONE, NULL, NULL);
    if (account == NULL)
    {
        PERR("Unable to find account for id %s", data.account_id);
        return 0;
    }
    /***** Validate the input strings to ensure utf8 *****/
    if (data.name_valid)
        gnc_utf8_strip_invalid(data.name);
    if (data.memo_valid)
        gnc_utf8_strip_invalid(data.memo);
    if (data.check_number_valid)
        gnc_utf8_strip_invalid(data.check_number);
    if (data.reference_number_valid)
        gnc_utf8_strip_invalid(data.reference_number);

    /***** Create the transaction and setup transaction data *******/
    book = gnc_account_get_book(account);
    transaction = xaccMallocTransaction(book);
    xaccTransBeginEdit(transaction);

    /* Note: Unfortunately libofx <= 0.9.5 will not report a missing
     * date field as an invalid one. Instead, it will report it as
     * valid and return a completely bogus date. Starting with
     * libofx-0.9.6 (not yet released as of 2012-09-09), it will still
     * be reported as valid but at least the date integer itself is
     * just plain zero. */
    if (data.date_posted_valid && (data.date_posted != 0))
    {
        /* The hopeful case: We have a posted_date */
        xaccTransSetDatePostedSecsNormalized(transaction, data.date_posted);
    } else if (data.date_initiated_valid && (data.date_initiated != 0))
    {
        /* No posted date? Maybe we have an initiated_date */
        xaccTransSetDatePostedSecsNormalized(transaction, data.date_initiated);
    }
    else
    {
        /* Uh no, no valid date. As a workaround use today's date */
        xaccTransSetDatePostedSecsNormalized(transaction, current_time);
    }

    xaccTransSetDateEnteredSecs(transaction, current_time);

    /* Put transaction name in Description, or memo if name unavailable */
    if (data.name_valid)
    {
        xaccTransSetDescription(transaction, data.name);
    }
    else if (data.memo_valid)
    {
        xaccTransSetDescription(transaction, data.memo);
    }

    /* Put everything else in the Notes field */
    notes = g_strdup_printf("OFX ext. info: ");

    if (data.transactiontype_valid)
    {
        tmp = notes;
        notes = g_strdup_printf("%s%s%s", tmp, "|Trans type:",
                                gnc_ofx_ttype_to_string(data.transactiontype));
        g_free(tmp);
    }

    if (data.invtransactiontype_valid)
    {
        tmp = notes;
        notes = g_strdup_printf("%s%s%s", tmp, "|Investment Trans type:",
                                gnc_ofx_invttype_to_str(data.invtransactiontype));
        g_free(tmp);
    }
    if (data.memo_valid && data.name_valid) /* Copy only if memo wasn't put in Description */
    {
        tmp = notes;
        notes = g_strdup_printf("%s%s%s", tmp, "|Memo:", data.memo);
        g_free(tmp);
    }
    if (data.date_funds_available_valid)
    {
        Timespec ts;
        timespecFromTime64(&ts, data.date_funds_available);
        gnc_timespec_to_iso8601_buff (ts, dest_string);
        tmp = notes;
        notes = g_strdup_printf("%s%s%s", tmp, "|Date funds available:", dest_string);
        g_free(tmp);
    }
    if (data.server_transaction_id_valid)
    {
        tmp = notes;
        notes = g_strdup_printf("%s%s%s", tmp, "|Server trans ID (conf. number):", data.server_transaction_id);
        g_free(tmp);
    }
    if (data.standard_industrial_code_valid)
    {
        tmp = notes;
        notes = g_strdup_printf("%s%s%ld", tmp, "|Standard Industrial Code:", data.standard_industrial_code);
        g_free(tmp);

    }
    if (data.payee_id_valid)
    {
        tmp = notes;
        notes = g_strdup_printf("%s%s%s", tmp, "|Payee ID:", data.payee_id);
        g_free(tmp);
    }

    //PERR("WRITEME: GnuCash ofx_proc_transaction():Add PAYEE and ADRESS here once supported by libofx! Notes=%s\n", notes);

    /* Ideally, gnucash should process the corrected transactions */
    if (data.fi_id_corrected_valid)
    {
        PERR("WRITEME: GnuCash ofx_proc_transaction(): WARNING: This transaction corrected a previous transaction, but we created a new one instead!\n");
        tmp = notes;
        notes = g_strdup_printf("%s%s%s%s", tmp, "|This corrects transaction #", data.fi_id_corrected, "but GnuCash didn't process the correction!");
        g_free(tmp);
    }
    xaccTransSetNotes(transaction, notes);
    g_free(notes);

    if (data.account_ptr && data.account_ptr->currency_valid)
    {
        DEBUG("Currency from libofx: %s", data.account_ptr->currency);
        currency = gnc_commodity_table_lookup( gnc_get_current_commodities (),
                                               GNC_COMMODITY_NS_CURRENCY,
                                               data.account_ptr->currency);
    }
    else
    {
        DEBUG("Currency from libofx unavailable, defaulting to account's default");
        currency = xaccAccountGetCommodity(account);
    }

    xaccTransSetCurrency(transaction, currency);
    if (data.amount_valid)
    {
        if (!data.invtransactiontype_valid)
        {
            /***** Process a normal transaction ******/
            DEBUG("Adding split; Ordinary banking transaction, money flows from or into the source account");
            split = xaccMallocSplit(book);
            xaccTransAppendSplit(transaction, split);
            xaccAccountInsertSplit(account, split);

            gnc_amount = gnc_ofx_numeric_from_double_txn(data.amount, transaction);
            xaccSplitSetBaseValue(split, gnc_amount, xaccTransGetCurrency(transaction));

            /* set tran-num and/or split-action per book option */
            if (data.check_number_valid)
            {
                gnc_set_num_action(transaction, split, data.check_number, NULL);
            }
            else if (data.reference_number_valid)
            {
                gnc_set_num_action(transaction, split, data.reference_number, NULL);
            }
            /* Also put the ofx transaction's memo in the
             * split's memo field */
            if (data.memo_valid)
            {
                xaccSplitSetMemo(split, data.memo);
            }
            if (data.fi_id_valid)
            {
                gnc_import_set_split_online_id(split, data.fi_id);
            }
        }

        else if (data.unique_id_valid
                 && data.security_data_valid
                 && data.security_data_ptr != NULL
                 && data.security_data_ptr->secname_valid)
        {
            gboolean choosing_account = TRUE;
            /********* Process an investment transaction **********/
            /* Note that the ACCT_TYPE_STOCK account type
               should be replaced with something derived from
               data.invtranstype*/

            // We have an investment transaction. First select the correct commodity.
            investment_commodity = gnc_import_select_commodity(data.unique_id,
                                   FALSE,
                                   NULL,
                                   NULL);
            if (investment_commodity != NULL)
            {
                // As we now have the commodity, select the account with that commodity.

                investment_account_text = g_strdup_printf( /* This string is a default account
                                                              name. It MUST NOT contain the
                                                              character ':' anywhere in it or
                                                              in any translations.  */
                                              _("Stock account for security \"%s\""),
                                              data.security_data_ptr->secname);

                investment_account_onlineid = g_strdup_printf( "%s%s", data.account_id, data.unique_id);
                investment_account = gnc_import_select_account(NULL,
                                                               investment_account_onlineid,
                                                               1,
                                                               investment_account_text,
                                                               investment_commodity,
                                                               ACCT_TYPE_STOCK,
                                                               NULL,
                                                               NULL);

                // but use it only if that's really the right commodity
                if (investment_account
                        && xaccAccountGetCommodity(investment_account) != investment_commodity)
                    investment_account = NULL;

                // Loop until we either have an account, or the user pressed Cancel
                while (!investment_account && choosing_account)
                {
                    // No account with correct commodity automatically found.

                    // But are we in auto-create mode and already know a parent?
                    if (auto_create_commodity && ofx_parent_account)
                    {
                        // Yes, so use that as parent when auto-creating the new account below.
                        investment_account = ofx_parent_account;
                    }
                    else
                    {
                        // Let the user choose an account
                        investment_account = gnc_import_select_account(
                                                 gnc_gen_trans_list_widget(gnc_ofx_importer_gui),
                                                 data.unique_id,
                                                 TRUE,
                                                 investment_account_text,
                                                 investment_commodity,
                                                 ACCT_TYPE_STOCK,
                                                 NULL,
                                                 &choosing_account);
                    }
                    // Does the chosen account have the right commodity?
                    if (investment_account && xaccAccountGetCommodity(investment_account) != investment_commodity)
                    {
                        if (auto_create_commodity
                                && xaccAccountTypesCompatible(xaccAccountGetType(investment_account),
                                                              ACCT_TYPE_STOCK))
                        {
                            // The user chose an account, but it does
                            // not have the right commodity. Also,
                            // auto-creation is on. Hence, we create a
                            // new child account of the selected one,
                            // and this one will have the right
                            // commodity.
                            Account *parent_account = investment_account;
                            investment_account =
                                gnc_ofx_new_account(investment_account_text,
                                                    investment_commodity,
                                                    parent_account,
                                                    ACCT_TYPE_STOCK);
                            if (investment_account)
                            {
                                gnc_import_set_acc_online_id(investment_account, data.unique_id);
                                choosing_account = FALSE;
                                ofx_parent_account = parent_account;
                            }
                            else
                            {
                                ofx_parent_account = NULL;
                            }
                        }
                        else
                        {
                            // No account with matching commodity. Ask the user
                            // whether to continue or abort.
                            choosing_account =
                                gnc_verify_dialog(
                                    gnc_gen_trans_list_widget(gnc_ofx_importer_gui), TRUE,
                                    "The chosen account \"%s\" does not have the correct "
                                    "currency/security \"%s\" (it has \"%s\" instead). "
                                    "This account cannot be used. "
                                    "Do you want to choose again?",
                                    xaccAccountGetName(investment_account),
                                    gnc_commodity_get_fullname(investment_commodity),
                                    gnc_commodity_get_fullname(xaccAccountGetCommodity(investment_account)));
                            // We must also delete the online_id that was set in gnc_import_select_account()
                            gnc_import_set_acc_online_id(investment_account, "");
                            investment_account = NULL;
                        }
                    }
                }
                if (!investment_account)
                {
                    PERR("No investment account found for text: %s\n", investment_account_text);
                }
                g_free (investment_account_text);
                g_free (investment_account_onlineid);
                investment_account_text = NULL;

                if (investment_account != NULL &&
                        data.unitprice_valid &&
                        data.units_valid &&
                        ( data.invtransactiontype != OFX_INCOME ) )
                {
                    DEBUG("Adding investment split; Money flows from or into the stock account");
                    split = xaccMallocSplit(book);
                    xaccTransAppendSplit(transaction, split);
                    xaccAccountInsertSplit(investment_account, split);

                    gnc_amount = gnc_ofx_numeric_from_double (ofx_get_investment_amount(&data),
                                 investment_commodity);
                    gnc_units = gnc_ofx_numeric_from_double (data.units, investment_commodity);
                    xaccSplitSetAmount(split, gnc_units);
                    xaccSplitSetValue(split, gnc_amount);

                    /* set tran-num and/or split-action per book option */
                    if (data.check_number_valid)
                    {
                        gnc_set_num_action(transaction, split, data.check_number, NULL);
                    }
                    else if (data.reference_number_valid)
                    {
                        gnc_set_num_action(transaction, split,
                                                data.reference_number, NULL);
                    }
                    if (data.security_data_ptr->memo_valid)
                    {
                        xaccSplitSetMemo(split, data.security_data_ptr->memo);
                    }
                    if (data.fi_id_valid)
                    {
                        gnc_import_set_split_online_id(split, data.fi_id);
                    }
                }
                else
                {
                    if (investment_account)
                        PERR("The investment account, units or unitprice was not found for the investment transaction");
                }
            }
            else
            {
                PERR("Commodity not found for the investment transaction");
            }

            if (data.invtransactiontype_valid && investment_account)
            {
                if (data.invtransactiontype == OFX_REINVEST
                        || data.invtransactiontype == OFX_INCOME)
                {
                    DEBUG("Now let's find an account for the destination split");

                    income_account = gnc_ofx_kvp_get_assoc_account(investment_account);

                    if (income_account == NULL)
                    {
                        DEBUG("Couldn't find an associated income account");
                        investment_account_text = g_strdup_printf( /* This string is a default account
                                                                      name. It MUST NOT contain the
                                                                      character ':' anywhere in it or
                                                                      in any translations.  */
                                                      _("Income account for security \"%s\""),
                                                      data.security_data_ptr->secname);
                        income_account = gnc_import_select_account(
                                             gnc_gen_trans_list_widget(gnc_ofx_importer_gui),
                                             NULL,
                                             1,
                                             investment_account_text,
                                             currency,
                                             ACCT_TYPE_INCOME,
                                             NULL,
                                             NULL);
                        gnc_ofx_kvp_set_assoc_account(investment_account,
                                                      income_account);
                        DEBUG("KVP written");

                    }
                    else
                    {
                        DEBUG("Found at least one associated income account");
                    }
                }
                if (income_account != NULL &&
                        data.invtransactiontype == OFX_REINVEST)
                {
                    DEBUG("Adding investment split; Money flows from the income account");
                    split = xaccMallocSplit(book);
                    xaccTransAppendSplit(transaction, split);
                    xaccAccountInsertSplit(income_account, split);

                    gnc_amount = gnc_ofx_numeric_from_double_txn (data.amount, transaction);
                    xaccSplitSetBaseValue(split, gnc_amount, xaccTransGetCurrency(transaction));

                    // Set split memo from ofx transaction name or memo
                    gnc_ofx_set_split_memo(&data, split);
                }
                if (income_account != NULL &&
                        data.invtransactiontype == OFX_INCOME)
                {
                    DEBUG("Adding investment split; Money flows from the income account");
                    split = xaccMallocSplit(book);
                    xaccTransAppendSplit(transaction, split);
                    xaccAccountInsertSplit(income_account, split);

                    gnc_amount = gnc_ofx_numeric_from_double_txn (-data.amount,/*OFX_INCOME amounts come in as positive numbers*/
                                 transaction);
                    xaccSplitSetBaseValue(split, gnc_amount, xaccTransGetCurrency(transaction));

                    // Set split memo from ofx transaction name or memo
                    gnc_ofx_set_split_memo(&data, split);
                }
            }

            if (data.invtransactiontype_valid
                    && data.invtransactiontype != OFX_REINVEST)
            {
                DEBUG("Adding investment split; Money flows from or to the cash account");
                split = xaccMallocSplit(book);
                xaccTransAppendSplit(transaction, split);
                xaccAccountInsertSplit(account, split);

                gnc_amount = gnc_ofx_numeric_from_double_txn(
                                 -ofx_get_investment_amount(&data), transaction);
                xaccSplitSetBaseValue(split, gnc_amount,
                                      xaccTransGetCurrency(transaction));

                // Set split memo from ofx transaction name or memo
                gnc_ofx_set_split_memo(&data, split);
            }
        }

        /* Send transaction to importer GUI. */
        if (xaccTransCountSplits(transaction) > 0)
        {
            DEBUG("%d splits sent to the importer gui", xaccTransCountSplits(transaction));
            gnc_gen_trans_list_add_trans (gnc_ofx_importer_gui, transaction);
        }
        else
        {
            PERR("No splits in transaction (missing account?), ignoring.");
            xaccTransDestroy(transaction);
            xaccTransCommitEdit(transaction);
        }
    }
    else
    {
        PERR("The transaction doesn't have a valid amount");
        xaccTransDestroy(transaction);
        xaccTransCommitEdit(transaction);
    }

    return 0;
}//end ofx_proc_transaction()