コード例 #1
0
ファイル: dialog-payment.c プロジェクト: laguz/gnucash
gboolean gnc_ui_payment_is_customer_payment(const Transaction *txn)
{
    SplitList *slist;
    gboolean result = TRUE;

    Split *assetaccount_split;
    gnc_numeric amount;

    if (!txn)
        return result;

    // We require the txn to have one split in an A/R or A/P account.

    slist = xaccTransGetSplitList(txn);
    if (!slist)
        return result;
    if (countAssetAccounts(slist) == 0)
    {
        g_message("No asset splits in txn \"%s\"; cannot use this for assigning a payment.",
                  xaccTransGetDescription(txn));
        return result;
    }

    assetaccount_split = getFirstAssetAccountSplit(slist);
    amount = xaccSplitGetValue(assetaccount_split);
    result = gnc_numeric_positive_p(amount); // positive amounts == customer
    //g_message("Amount=%s", gnc_numeric_to_string(amount));
    return result;
}
コード例 #2
0
/** Add a transaction to the register.
 *
 *  Virtual cells are set up to hold the data, beginning at @a vcell_loc and
 *  continuing downward in the same virtual column. The first virtual cell
 *  will be a "leading" cell, followed by a virtual cell for each split of
 *  transaction @a trans. For example, the "transaction journal" register
 *  style keeps all the transaction-level data and totals in the leading
 *  cell, and all split-level data in the cells that follow.
 *
 *  Each of these cells will remember the GncGUID of the ::Split to which it
 *  is associated. The leading virtual cell will be assigned the GncGUID of
 *  the "anchoring split" specified by @a split.
 *
 *  Optionally an extra, empty virtual cell will be assigned to no split.
 *  This should not be confused with the "blank split", because this cell is
 *  not tied to any split at all, not even the "blank split". A null GncGUID
 *  is assigned to it.
 *
 *  The caller can find out which virtual row was used for a particular virtual
 *  cell by using parameters @a find_trans, @a find_split, and @a find_class.
 *  If a match is found, the row is returned in @a new_split_row. Otherwise,
 *  @a new_split_row is not changed.
 *  - To find the virtual cell for a particular split, set @a find_split to
 *    the target ::Split and @a find_class to ::CURSOR_CLASS_SPLIT.
 *  - To find the empty row, set @a find_trans equal to @a trans, @a find_split
 *    to @c NULL, and @a find_class to ::CURSOR_CLASS_SPLIT.
 *  - The leading virtual cell is always placed in the row specified by
 *    @a vcell_loc, but this will not be returned in @a new_split_row unless
 *    @a find_split is set to the anchoring split and @a find_class is not
 *    ::CURSOR_CLASS_SPLIT.
 *
 *  @param reg a ::SplitRegister
 *
 *  @param trans the transaction to add to the register
 *
 *  @param split a ::Split to use as the "anchoring split" in the "leading"
 *  virtual cell
 *
 *  @param lead_cursor the cursor to use for the "leading" virtual cell
 *
 *  @param split_cursor the cursor to use in the split rows that follow
 *
 *  @param visible_splits @c TRUE to make the split rows visible, @c FALSE
 *  otherwise
 *
 *  @param start_primary_color @c TRUE to use the primary color for the
 *  "leading" row, @c FALSE otherwise
 *
 *  @param add_empty @c TRUE if an empty row should be added, @c FALSE
 *  otherwise
 *
 *  @param find_trans the transaction parameter for row searching
 *
 *  @param find_split the split parameter for row searching
 *
 *  @param find_class the cursor class parameter for row searching
 *
 *  @param new_split_row a pointer to be filled with the matching virtual row.
 *
 *  @param vcell_loc the location to begin setting virtual cells. The row
 *  will be changed to the row below the last row filled.
 */
static void
gnc_split_register_add_transaction (SplitRegister *reg,
                                    Transaction *trans,
                                    Split *split,
                                    CellBlock *lead_cursor,
                                    CellBlock *split_cursor,
                                    gboolean visible_splits,
                                    gboolean start_primary_color,
                                    gboolean add_empty,
                                    Transaction *find_trans,
                                    Split *find_split,
                                    CursorClass find_class,
                                    int *new_split_row,
                                    VirtualCellLocation *vcell_loc)
{
    GList *node;

    g_return_if_fail(reg);
    g_return_if_fail(vcell_loc);

    if (split == find_split)
        *new_split_row = vcell_loc->virt_row;

    /* Set the "leading" virtual cell. */
    gnc_table_set_vcell (reg->table, lead_cursor, xaccSplitGetGUID (split),
                         TRUE, start_primary_color, *vcell_loc);
    vcell_loc->virt_row++;

    /* Continue setting up virtual cells in a column, using a row for each
     * split in the transaction. */
    for (node = xaccTransGetSplitList (trans); node; node = node->next)
    {
        Split *secondary = node->data;

        if (!xaccTransStillHasSplit(trans, secondary)) continue;
        if (secondary == find_split && find_class == CURSOR_CLASS_SPLIT)
            *new_split_row = vcell_loc->virt_row;

        gnc_table_set_vcell (reg->table, split_cursor,
                             xaccSplitGetGUID (secondary),
                             visible_splits, TRUE, *vcell_loc);
        vcell_loc->virt_row++;
    }

    /* If requested, add an empty split row at the end. */
    if (add_empty)
    {
        if (find_trans == trans && find_split == NULL &&
                find_class == CURSOR_CLASS_SPLIT)
            *new_split_row = vcell_loc->virt_row;

        gnc_table_set_vcell(reg->table, split_cursor, xaccSplitGetGUID(NULL),
                            FALSE, TRUE, *vcell_loc);
        vcell_loc->virt_row++;
    }
}
コード例 #3
0
ファイル: gnc-transaction-xml-v2.c プロジェクト: 573/gnucash
static void
add_trans_splits(xmlNodePtr node, Transaction *trn)
{
    GList *n;
    xmlNodePtr toaddto;

    toaddto = xmlNewChild(node, NULL, BAD_CAST "trn:splits", NULL);

    for (n = xaccTransGetSplitList(trn); n; n = n->next)
    {
        Split *s = n->data;
        xmlAddChild(toaddto, split_to_dom_tree("trn:split", s));
    }
}
コード例 #4
0
ファイル: dialog-payment.c プロジェクト: laguz/gnucash
PaymentWindow * gnc_ui_payment_new_with_txn (GncOwner *owner, Transaction *txn)
{
    SplitList *slist;

    Split *assetaccount_split;
    Split *postaccount_split;
    gnc_numeric amount;
    PaymentWindow *pw;

    if (!txn)
        return NULL;

    // We require the txn to have one split in an Asset account.

    slist = xaccTransGetSplitList(txn);
    if (!slist)
        return NULL;
    if (countAssetAccounts(slist) == 0)
    {
        g_message("No asset splits in txn \"%s\"; cannot use this for assigning a payment.",
                  xaccTransGetDescription(txn));
        return NULL;
    }

    assetaccount_split = getFirstAssetAccountSplit(slist);
    postaccount_split = getFirstAPARAccountSplit(slist); // watch out: Might be NULL
    amount = xaccSplitGetValue(assetaccount_split);

    pw = gnc_ui_payment_new(owner,
                            qof_instance_get_book(QOF_INSTANCE(txn)));
    g_assert(assetaccount_split); // we can rely on this because of the countAssetAccounts() check above
    g_debug("Amount=%s", gnc_numeric_to_string(amount));

    // Fill in the values from the given txn
    pw->pre_existing_txn = txn;
    gnc_ui_payment_window_set_num(pw, gnc_get_num_action(txn, assetaccount_split));
    gnc_ui_payment_window_set_memo(pw, xaccTransGetDescription(txn));
    {
        GDate txn_date = xaccTransGetDatePostedGDate (txn);
        gnc_ui_payment_window_set_date(pw, &txn_date);
    }
    gnc_ui_payment_window_set_amount(pw, amount);
    gnc_ui_payment_window_set_xferaccount(pw, xaccSplitGetAccount(assetaccount_split));
    if (postaccount_split)
        gnc_ui_payment_window_set_postaccount(pw, xaccSplitGetAccount(postaccount_split));

    return pw;
}
コード例 #5
0
static gboolean // Can't be bool because of signature for xaccAccountTreeForEach
write_tx (Transaction* tx, gpointer data)
{
    auto s = static_cast<write_objects_t*>(data);

    g_return_val_if_fail (tx != NULL, 0);
    g_return_val_if_fail (data != NULL, 0);

    s->commit (QOF_INSTANCE (tx));
    auto splitbe = s->be->get_object_backend(GNC_ID_SPLIT);
    for (auto split_node = xaccTransGetSplitList (tx);
         split_node != nullptr && s->is_ok;
         split_node = g_list_next (split_node))
    {
        s->is_ok = splitbe->commit(s->be, QOF_INSTANCE(split_node->data));
    }
    s->be->update_progress ();
    return (s->is_ok ? 0 : 1);
}
コード例 #6
0
/**
 * Deletes all of the splits for a transaction
 *
 * @param be SQL backend
 * @param pTx Transaction
 * @return TRUE if successful, FALSE if unsuccessful
 */
static gboolean
delete_splits( GncSqlBackend* be, Transaction* pTx )
{
    split_info_t split_info;

    g_return_val_if_fail( be != NULL, FALSE );
    g_return_val_if_fail( pTx != NULL, FALSE );

    if ( !gnc_sql_do_db_operation( be, OP_DB_DELETE, SPLIT_TABLE,
                                   SPLIT_TABLE, pTx, tx_guid_col_table ) )
    {
        return FALSE;
    }
    split_info.be = be;
    split_info.is_ok = TRUE;

    g_list_foreach( xaccTransGetSplitList( pTx ), delete_split_slots_cb, &split_info );

    return split_info.is_ok;
}
コード例 #7
0
ファイル: gncOwner.c プロジェクト: ShawnMcGough/gnucash
/* Find an existing lot link transaction in the given lot
 * Only use a lot link that already links at least two
 * documents (to avoid perpetuating the lot link proliferation
 * that happened in 2.6.0-2.6.3).
 */
static Transaction *
get_ll_transaction_from_lot (GNCLot *lot)
{
    SplitList *ls_iter;

    /* This should really only be called on a document lot */
    if (!gncInvoiceGetInvoiceFromLot (lot))
        return NULL;

    /* The given lot is a valid document lot. Now iterate over all
     * other lot links in this lot to find one more document lot.
     */
    for (ls_iter = gnc_lot_get_split_list (lot); ls_iter; ls_iter = ls_iter->next)
    {
        Split *ls = ls_iter->data;
        Transaction *ll_txn = xaccSplitGetParent (ls);
        SplitList *ts_iter;

        if (xaccTransGetTxnType (ll_txn) != TXN_TYPE_LINK)
            continue;

        for (ts_iter = xaccTransGetSplitList (ll_txn); ts_iter; ts_iter = ts_iter->next)
        {
            Split *ts = ts_iter->data;
            GNCLot *tslot = xaccSplitGetLot (ts);

            if (!tslot)
                continue;

            if (tslot == lot)
                continue;

            if (gncInvoiceGetInvoiceFromLot (lot))
                return ll_txn; /* Got one more document lot - mission accomplished */
        }
    }

    /* The lot doesn't have an ll_txn with the requested criteria... */
    return NULL;
}
コード例 #8
0
ファイル: dialog-sx-from-trans.c プロジェクト: Bob-IT/gnucash
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;
}
コード例 #9
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);
    }
}
コード例 #10
0
void
gnc_split_register_load (SplitRegister *reg, GList * slist,
                         Account *default_account)
{
    SRInfo *info;
    Transaction *pending_trans;
    CursorBuffer *cursor_buffer;
    GHashTable *trans_table = NULL;
    CellBlock *cursor_header;
    CellBlock *lead_cursor;
    CellBlock *split_cursor;
    Transaction *blank_trans;
    Transaction *find_trans;
    Transaction *trans;
    CursorClass find_class;
    Split *find_trans_split;
    Split *blank_split;
    Split *find_split;
    Split *split;
    Table *table;
    GList *node;

    gboolean start_primary_color = TRUE;
    gboolean found_pending = FALSE;
    gboolean need_divider_upper = FALSE;
    gboolean found_divider_upper = FALSE;
    gboolean found_divider = FALSE;
    gboolean has_last_num = FALSE;
    gboolean multi_line;
    gboolean dynamic;
    gboolean we_own_slist = FALSE;
    gboolean use_autoreadonly = qof_book_uses_autoreadonly(gnc_get_current_book());

    VirtualCellLocation vcell_loc;
    VirtualLocation save_loc;

    int new_trans_split_row = -1;
    int new_trans_row = -1;
    int new_split_row = -1;
    time64 present, autoreadonly_time = 0;

    g_return_if_fail(reg);
    table = reg->table;
    g_return_if_fail(table);
    info = gnc_split_register_get_info (reg);
    g_return_if_fail(info);

    ENTER("reg=%p, slist=%p, default_account=%p", reg, slist, default_account);

    blank_split = xaccSplitLookup (&info->blank_split_guid,
                                   gnc_get_current_book ());

    pending_trans = xaccTransLookup (&info->pending_trans_guid,
                                     gnc_get_current_book ());

    /* make sure we have a blank split */
    if (blank_split == NULL)
    {
	/* Wouldn't it be a bug to open the new transaction if there was
	 * already a pending transaction?
	*/
	g_assert(pending_trans == NULL);
	blank_split = create_blank_split (default_account, info);
    }
    blank_trans = xaccSplitGetParent (blank_split);

    DEBUG("blank_split=%p, blank_trans=%p, pending_trans=%p",
          blank_split, blank_trans, pending_trans);

    info->default_account = *xaccAccountGetGUID (default_account);

    // gnc_table_leave_update (table, table->current_cursor_loc);

    multi_line = (reg->style == REG_STYLE_JOURNAL);
    dynamic    = (reg->style == REG_STYLE_AUTO_LEDGER);

    lead_cursor = gnc_split_register_get_passive_cursor (reg);
    split_cursor = gnc_table_layout_get_cursor (table->layout, CURSOR_SPLIT);

    /* figure out where we are going to. */
    if (info->traverse_to_new)
    {
        find_trans = blank_trans;
        find_split = NULL;
        find_trans_split = blank_split;
        find_class = CURSOR_CLASS_SPLIT;
    }
    else
    {
        find_trans = info->cursor_hint_trans;
        find_split = info->cursor_hint_split;
        find_trans_split = info->cursor_hint_trans_split;
        find_class = info->cursor_hint_cursor_class;
    }

    save_loc = table->current_cursor_loc;

    /* If the current cursor has changed we save the values for later
     * possible restoration. */
    if (gnc_table_current_cursor_changed (table, TRUE) &&
            (find_split == gnc_split_register_get_current_split (reg)))
    {
        cursor_buffer = gnc_cursor_buffer_new ();
        gnc_table_save_current_cursor (table, cursor_buffer);
    }
    else
        cursor_buffer = NULL;

    /* disable move callback -- we don't want the cascade of
     * callbacks while we are fiddling with loading the register */
    gnc_table_control_allow_move (table->control, FALSE);

    /* invalidate the cursor */
    {
        VirtualLocation virt_loc;

        gnc_virtual_location_init(&virt_loc);
        gnc_table_move_cursor_gui (table, virt_loc);
    }

    /* make sure that the header is loaded */
    vcell_loc.virt_row = 0;
    vcell_loc.virt_col = 0;
    cursor_header = gnc_table_layout_get_cursor (table->layout, CURSOR_HEADER);
    gnc_table_set_vcell (table, cursor_header, NULL, TRUE, TRUE, vcell_loc);
    vcell_loc.virt_row++;

    /* get the current time and reset the dividing row */
    present = gnc_time64_get_today_end ();
    if (use_autoreadonly)
    {
        GDate *d = qof_book_get_autoreadonly_gdate(gnc_get_current_book());
        // "d" is NULL if use_autoreadonly is FALSE
        autoreadonly_time = d ? timespecToTime64(gdate_to_timespec(*d)) : 0;
        g_date_free(d);
    }

    if (info->first_pass)
    {
        if (default_account)
        {
            const char *last_num = xaccAccountGetLastNum (default_account);

            if (last_num)
            {
                NumCell *cell;

                cell = (NumCell *) gnc_table_layout_get_cell(table->layout, NUM_CELL);
                gnc_num_cell_set_last_num (cell, last_num);
                has_last_num = TRUE;
            }
        }

        /* load up account names into the transfer combobox menus */
        gnc_split_register_load_xfer_cells (reg, default_account);
        gnc_split_register_load_recn_cells (reg);
        gnc_split_register_load_type_cells (reg);
    }

    if (info->separator_changed)
	change_account_separator (info, table, reg);

    table->model->dividing_row_upper = -1;
    table->model->dividing_row = -1;

    // Ensure that the transaction and splits being edited are in the split
    // list we're about to load.
    if (pending_trans != NULL)
    {
        for (node = xaccTransGetSplitList(pending_trans); node; node = node->next)
        {
            Split *pending_split = (Split*)node->data;
            if (!xaccTransStillHasSplit(pending_trans, pending_split)) continue;
            if (g_list_find(slist, pending_split) != NULL)
                continue;

            if (g_list_find_custom(slist, pending_trans,
                                   _find_split_with_parent_txn) != NULL)
                continue;

            if (!we_own_slist)
            {
                // lazy-copy
                slist = g_list_copy(slist);
                we_own_slist = TRUE;
            }
            slist = g_list_append(slist, pending_split);
        }
    }

    if (multi_line)
        trans_table = g_hash_table_new (g_direct_hash, g_direct_equal);

    /* populate the table */
    for (node = slist; node; node = node->next)
    {
        split = node->data;
        trans = xaccSplitGetParent (split);

        if (!xaccTransStillHasSplit(trans, split))
            continue;

        if (pending_trans == trans)
            found_pending = TRUE;
	/* If the transaction has only one split, and it's not our
	 * pending_trans, then it's another register's blank split and
	 * we don't want to see it.
	 */
	else if (xaccTransCountSplits (trans) == 1 &&
		 xaccSplitGetAccount (split) == NULL)
	    continue;


        /* Do not load splits from the blank transaction. */
        if (trans == blank_trans)
            continue;

        if (multi_line)
        {
            /* Skip this split if its transaction has already been loaded. */
            if (g_hash_table_lookup (trans_table, trans))
                continue;

            g_hash_table_insert (trans_table, trans, trans);
        }

        if (info->show_present_divider &&
                use_autoreadonly &&
                !found_divider_upper)
        {
            if (xaccTransGetDate (trans) >= autoreadonly_time)
            {
                table->model->dividing_row_upper = vcell_loc.virt_row;
                found_divider_upper = TRUE;
            }
            else
            {
                need_divider_upper = TRUE;
            }
        }

        if (info->show_present_divider &&
                !found_divider &&
                (xaccTransGetDate (trans) > present))
        {
            table->model->dividing_row = vcell_loc.virt_row;
            found_divider = TRUE;
        }

        /* If this is the first load of the register,
         * fill up the quickfill cells. */
        if (info->first_pass)
            add_quickfill_completions(reg->table->layout, trans, split, has_last_num);

        if (trans == find_trans)
            new_trans_row = vcell_loc.virt_row;

        if (split == find_trans_split)
            new_trans_split_row = vcell_loc.virt_row;

        gnc_split_register_add_transaction (reg, trans, split,
                                            lead_cursor, split_cursor,
                                            multi_line, start_primary_color,
                                            TRUE,
                                            find_trans, find_split, find_class,
                                            &new_split_row, &vcell_loc);

        if (!multi_line)
            start_primary_color = !start_primary_color;
    }

    if (multi_line)
        g_hash_table_destroy (trans_table);

    /* add the blank split at the end. */
    if (pending_trans == blank_trans)
        found_pending = TRUE;

    /* No upper divider yet? Store it now */
    if (info->show_present_divider &&
            use_autoreadonly &&
            !found_divider_upper && need_divider_upper)
    {
        table->model->dividing_row_upper = vcell_loc.virt_row;
        found_divider_upper = TRUE;
    }

    if (blank_trans == find_trans)
        new_trans_row = vcell_loc.virt_row;

    if (blank_split == find_trans_split)
        new_trans_split_row = vcell_loc.virt_row;

    /* If we didn't find the pending transaction, it was removed
     * from the account. */
    if (!found_pending)
    {
        info->pending_trans_guid = *guid_null ();
        if (xaccTransIsOpen (pending_trans))
            xaccTransCommitEdit (pending_trans);
        else if (pending_trans)
            g_assert_not_reached();

        pending_trans = NULL;
    }

    /* go to blank on first pass */
    if (info->first_pass)
    {
        new_split_row = -1;
        new_trans_split_row = -1;
        new_trans_row = -1;

        save_loc.vcell_loc = vcell_loc;
        save_loc.phys_row_offset = 0;
        save_loc.phys_col_offset = 0;
    }

    gnc_split_register_add_transaction (reg, blank_trans, blank_split,
                                        lead_cursor, split_cursor,
                                        multi_line, start_primary_color,
                                        info->blank_split_edited, find_trans,
                                        find_split, find_class, &new_split_row,
                                        &vcell_loc);

    /* resize the table to the sizes we just counted above */
    /* num_virt_cols is always one. */
    gnc_table_set_size (table, vcell_loc.virt_row, 1);

    /* restore the cursor to its rightful position */
    {
        VirtualLocation trans_split_loc;

        if (new_split_row > 0)
            save_loc.vcell_loc.virt_row = new_split_row;
        else if (new_trans_split_row > 0)
            save_loc.vcell_loc.virt_row = new_trans_split_row;
        else if (new_trans_row > 0)
            save_loc.vcell_loc.virt_row = new_trans_row;

        trans_split_loc = save_loc;

	gnc_split_register_get_trans_split (reg, save_loc.vcell_loc,
					    &trans_split_loc.vcell_loc);

        if (dynamic || multi_line || info->trans_expanded)
        {
            gnc_table_set_virt_cell_cursor(
                table, trans_split_loc.vcell_loc,
                gnc_split_register_get_active_cursor (reg));
            gnc_split_register_set_trans_visible (reg, trans_split_loc.vcell_loc,
                                                  TRUE, multi_line);

            info->trans_expanded = (reg->style == REG_STYLE_LEDGER);
        }
        else
        {
            save_loc = trans_split_loc;
            info->trans_expanded = FALSE;
        }

        if (gnc_table_find_close_valid_cell (table, &save_loc, FALSE))
        {
            gnc_table_move_cursor_gui (table, save_loc);
            new_split_row = save_loc.vcell_loc.virt_row;

            if (find_split == gnc_split_register_get_current_split (reg))
                gnc_table_restore_current_cursor (table, cursor_buffer);
        }
    }
    gnc_cursor_buffer_destroy (cursor_buffer);
    cursor_buffer = NULL;

    update_info (info, reg);

    gnc_split_register_set_cell_fractions(
        reg, gnc_split_register_get_current_split (reg));

    gnc_table_refresh_gui (table, TRUE);

    gnc_split_register_show_trans (reg, table->current_cursor_loc.vcell_loc);

    /* enable callback for cursor user-driven moves */
    gnc_table_control_allow_move (table->control, TRUE);

    if (we_own_slist)
        g_list_free(slist);

    LEAVE(" ");
}
コード例 #11
0
static gboolean
save_transaction( GncSqlBackend* be, Transaction* pTx, gboolean do_save_splits )
{
    const GncGUID* guid;
    gint op;
    gboolean is_infant;
    QofInstance* inst;
    gboolean is_ok = TRUE;
    gchar* err = NULL;

    g_return_val_if_fail( be != NULL, FALSE );
    g_return_val_if_fail( pTx != NULL, FALSE );

    inst = QOF_INSTANCE(pTx);
    is_infant = qof_instance_get_infant( inst );
    if ( qof_instance_get_destroying( inst ) )
    {
        op = OP_DB_DELETE;
    }
    else if ( be->is_pristine_db || is_infant )
    {
        op = OP_DB_INSERT;
    }
    else
    {
        op = OP_DB_UPDATE;
    }

    if ( op != OP_DB_DELETE )
    {
        gnc_commodity *commodity = xaccTransGetCurrency( pTx );
        // Ensure the commodity is in the db
        is_ok = gnc_sql_save_commodity( be, commodity );
        if ( ! is_ok )
        {
            err = "Commodity save failed: Probably an invalid or missing currency";
            qof_backend_set_error( &be->be, ERR_BACKEND_DATA_CORRUPT);
        }
    }

    if ( is_ok )
    {
        is_ok = gnc_sql_do_db_operation( be, op, TRANSACTION_TABLE, GNC_ID_TRANS, pTx, tx_col_table );
        if ( ! is_ok )
        {
            err = "Transaction header save failed. Check trace log for SQL errors";
        }
    }

    if ( is_ok )
    {
        // Commit slots and splits
        guid = qof_instance_get_guid( inst );
        if ( !qof_instance_get_destroying(inst) )
        {
            is_ok = gnc_sql_slots_save( be, guid, is_infant, qof_instance_get_slots( inst ) );
            if ( ! is_ok )
            {
                err = "Slots save failed. Check trace log for SQL errors";
            }
            if ( is_ok && do_save_splits )
            {
                is_ok = save_splits( be, guid, xaccTransGetSplitList( pTx ) );
                if ( ! is_ok )
                {
                    err = "Split save failed. Check trace log for SQL errors";
                }
            }
        }
        else
        {
            is_ok = gnc_sql_slots_delete( be, guid );
            if ( ! is_ok )
            {
                err = "Slots delete failed. Check trace log for SQL errors";
            }
            if ( is_ok )
            {
                is_ok = delete_splits( be, pTx );
                if ( ! is_ok )
                {
                    err = "Split delete failed. Check trace log for SQL errors";
                }
            }
        }
    }
    if (! is_ok )
    {
        G_GNUC_UNUSED gchar *message1 = "Transaction %s dated %s in account %s not saved due to %s.%s";
        G_GNUC_UNUSED gchar *message2 = "\nDatabase may be corrupted, check your data carefully.";
        Split* split = xaccTransGetSplit( pTx, 0);
        Account *acc = xaccSplitGetAccount( split );
        /* FIXME: This needs to be implemented
        qof_error_format_secondary_text( GTK_MESSAGE_DIALOG( msg ),
        					  message1,
        					 xaccTransGetDescription( pTx ),
        					  qof_print_date( xaccTransGetDate( pTx ) ),
        					  xaccAccountGetName( acc ),
        					  err,
        					  message2 );
        */
        PERR( "Transaction %s dated %s in account %s not saved due to %s.\n",
              xaccTransGetDescription( pTx ),
              qof_print_date( xaccTransGetDate( pTx ) ),
              xaccAccountGetName( acc ),
              err );
    }
    return is_ok;
}
コード例 #12
0
ファイル: gnc-csv-model.c プロジェクト: geroldr/gnucash
/** Creates a list of transactions from parsed data. Transactions that
 * could be created from rows are placed in parse_data->transactions;
 * rows that fail are placed in parse_data->error_lines. (Note: there
 * is no way for this function to "fail," i.e. it only returns 0, so
 * it may be changed to a void function in the future.)
 * @param parse_data Data that is being parsed
 * @param account Account with which transactions are created
 * @param redo_errors TRUE to convert only error data, FALSE for all data
 * @return 0 on success, 1 on failure
 */
int gnc_csv_parse_to_trans (GncCsvParseData* parse_data, Account* account,
                           gboolean redo_errors)
{
    gboolean hasBalanceColumn;
    int i, j, max_cols = 0;
    GArray* column_types = parse_data->column_types;
    GList *error_lines = NULL, *begin_error_lines = NULL;

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

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

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

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

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

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

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

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

        trans_property_list_free (list);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    return 0;
}
コード例 #13
0
ファイル: reconcile-view.c プロジェクト: Gnucash/gnucash
static void
gnc_reconcile_view_toggle_children (Account *account, GNCReconcileView *view, Split *split)
{
    GList       *child_accounts, *node;
    Transaction *transaction;

    /*
     * Need to get all splits in this transaction and identify any that are
     * in the same hierarchy as the account being reconciled (not necessarily
     * the account this split is from.)
     *
     * For each of these splits toggle them all to the same state.
     */
    child_accounts = gnc_account_get_descendants (account);
    child_accounts = g_list_prepend (child_accounts, account);
    transaction = xaccSplitGetParent (split);
    for (node = xaccTransGetSplitList (transaction); node; node = node->next)
    {
        Split *other_split;
        Account *other_account;
        GNCReconcileView *current_view;

        GtkTreeModel *model;
        GtkTreeIter   iter;
        gboolean      valid;
        gpointer      pointer;

        other_split = node->data;
        other_account = xaccSplitGetAccount (other_split);
        if (other_split == split)
            continue;
        /* Check this 'other' account in in the same hierarchy */
        if (!g_list_find (child_accounts, other_account))
            continue;
        /* Search our sibling view for this split first.  We search the
         * sibling list first because that it where it is most likely to be.
         */
        current_view = view->sibling;
        if (!gnc_query_view_item_in_view (GNC_QUERY_VIEW (current_view), other_split))
        {
            /* Not in the sibling view, try this view */
            current_view = view;
            if (!gnc_query_view_item_in_view (GNC_QUERY_VIEW (current_view), other_split))
                /* We can't find it, nothing more I can do about it */
                continue;
        }

        /* Found the other split. Toggle the reconciled check mark in the view... */
        model = gtk_tree_view_get_model (GTK_TREE_VIEW (current_view));
        valid = gtk_tree_model_get_iter_first (model, &iter);

        while (valid)
        {
            // Walk through the list, reading each row
            gtk_tree_model_get (model, &iter, REC_POINTER, &pointer, -1);

            if(pointer == other_split)
            {
                gboolean toggled;
                gtk_tree_model_get (model, &iter, REC_RECN, &toggled, -1);
                gtk_list_store_set (GTK_LIST_STORE (model), &iter, REC_RECN, !toggled, -1);
                break;
            }

            valid = gtk_tree_model_iter_next (model, &iter);
        }

        /* ...and toggle its reconciled state in the internal hash */
        gnc_reconcile_view_toggle_split (current_view, other_split);
    }
    g_list_free (child_accounts);
}
コード例 #14
0
ファイル: ScrubBusiness.c プロジェクト: hawk-lord/gnucash
static gboolean
gncScrubLotLinks (GNCLot *scrub_lot)
{
    gboolean modified = FALSE, restart_needed = FALSE;
    SplitList *sls_iter = NULL;

scrub_start:
    restart_needed = FALSE;

    // Iterate over all splits in the lot
    for (sls_iter = gnc_lot_get_split_list (scrub_lot); sls_iter; sls_iter = sls_iter->next)
    {
        Split *sl_split = sls_iter->data;
        Transaction *ll_txn = NULL; // ll_txn = "Lot Link Transaction"
        SplitList *lts_iter = NULL;

        if (!sl_split)
            continue; // next scrub lot split

        // Only lot link transactions need to be scrubbed
        ll_txn = xaccSplitGetParent (sl_split);

        if (!ll_txn)
        {
            // Ooops - the split doesn't belong to any transaction !
            // This is not expected so issue a warning and continue with next split
            PWARN("Encountered a split in a business lot that's not part of any transaction. "
                  "This is unexpected! Skipping split %p.", sl_split);
            continue;
        }

        if (xaccTransGetTxnType (ll_txn) != TXN_TYPE_LINK)
            continue; // next scrub lot split

        // Iterate over all splits in the lot link transaction
        for (lts_iter = xaccTransGetSplitList (ll_txn); lts_iter; lts_iter = lts_iter->next)
        {
            Split *ll_txn_split = lts_iter->data; // These all refer to splits in the lot link transaction
            GNCLot *remote_lot = NULL; // lot at the other end of the lot link transaction
            gboolean sl_is_doc_lot, rl_is_doc_lot;

            if (!ll_txn_split)
                continue; // next lot link transaction split

            // Skip the split in the lot we're currently scrubbing
            if (sl_split == ll_txn_split)
                continue; // next lot link transaction split

            // Only splits of opposite signed values can be scrubbed
            if (gnc_numeric_positive_p (xaccSplitGetValue (sl_split)) ==
                    gnc_numeric_positive_p (xaccSplitGetValue (ll_txn_split)))
                continue; // next lot link transaction split

            // Find linked lot via split
            remote_lot = xaccSplitGetLot (ll_txn_split);
            if (!remote_lot)
            {
                // This is unexpected - write a warning message and skip this split
                PWARN("Encountered a Lot Link transaction with a split that's not in any lot. "
                      "This is unexpected! Skipping split %p from transaction %p.", ll_txn_split, ll_txn);
                continue;
            }

            sl_is_doc_lot = (gncInvoiceGetInvoiceFromLot (scrub_lot) != NULL);
            rl_is_doc_lot = (gncInvoiceGetInvoiceFromLot (remote_lot) != NULL);

            // Depending on the type of lots we're comparing, we need different actions
            // - Two document lots (an invoice and a credit note):
            //   Special treatment - look for all document lots linked via ll_txn
            //   and update the memo to be of more use to the uses.
            // - Two payment lots:
            //   (Part of) the link will be eliminated and instead (part of)
            //   one payment will be added to the other lot to keep the balance.
            //   If the payments are not equal in abs value part of the bigger payment
            //   will be moved to the smaller payment's lot.
            // - A document and a payment lot:
            //   (Part of) the link will be eliminated and instead (part of) the real
            //   payment will be added to the document lot to handle the payment.
            if (sl_is_doc_lot && rl_is_doc_lot)
                gncOwnerSetLotLinkMemo (ll_txn);
            else if (!sl_is_doc_lot && !rl_is_doc_lot)
            {
                gint cmp = gnc_numeric_compare (gnc_numeric_abs (xaccSplitGetValue (sl_split)),
                                                gnc_numeric_abs (xaccSplitGetValue (ll_txn_split)));
                if (cmp >= 0)
                    restart_needed = scrub_other_link (scrub_lot, sl_split, remote_lot, ll_txn_split);
                else
                    restart_needed = scrub_other_link (remote_lot, ll_txn_split, scrub_lot, sl_split);
            }
            else
            {
                GNCLot *doc_lot = sl_is_doc_lot ? scrub_lot : remote_lot;
                GNCLot *pay_lot = sl_is_doc_lot ? remote_lot : scrub_lot;
                Split *ll_doc_split = sl_is_doc_lot ? sl_split : ll_txn_split;
                Split *ll_pay_split = sl_is_doc_lot ? ll_txn_split : sl_split;
                // Ok, let's try to move a payment from pay_lot to doc_lot
                restart_needed = scrub_other_link (pay_lot, ll_pay_split, doc_lot, ll_doc_split);
            }

            // If we got here, the splits in our lot and ll_txn have been severely mixed up
            // And our iterator lists are probably no longer valid
            // So let's start over
            if (restart_needed)
            {
                modified = TRUE;
                goto scrub_start;
            }

        }
    }

    return modified;
}
コード例 #15
0
ファイル: csv-transactions-export.c プロジェクト: 573/gnucash
/*******************************************************
 * account_splits
 *
 * gather the splits / transactions for an account and
 * send them to a file
 *******************************************************/
static
void account_splits (CsvExportInfo *info, Account *acc, FILE *fh )
{
    Query   *q;
    GSList  *p1, *p2;
    GList   *splits;
    QofBook *book;

    gchar   *end_sep;
    gchar   *mid_sep;

    q = qof_query_create_for(GNC_ID_SPLIT);
    book = gnc_get_current_book();
    qof_query_set_book (q, book);

    /* Set up separators */
    if (info->use_quotes)
    {
        end_sep = "\"";
        mid_sep = g_strconcat ( "\"", info->separator_str, "\"", NULL);
    }
    else
    {
        end_sep = "";
        mid_sep = g_strconcat ( info->separator_str, NULL);
    }

    /* Sort by transaction date */
    p1 = g_slist_prepend (NULL, TRANS_DATE_POSTED);
    p1 = g_slist_prepend (p1, SPLIT_TRANS);
    p2 = g_slist_prepend (NULL, QUERY_DEFAULT_SORT);
    qof_query_set_sort_order (q, p1, p2, NULL);

    xaccQueryAddSingleAccountMatch (q, acc, QOF_QUERY_AND);
    xaccQueryAddDateMatchTT (q, TRUE, info->csvd.start_time, TRUE, info->csvd.end_time, QOF_QUERY_AND);

    /* Run the query */
    for (splits = qof_query_run(q); splits; splits = splits->next)
    {
        Split       *split;
        Transaction *trans;
        SplitList   *s_list;
        GList       *node;
        Split       *t_split;
        int          nSplits;
        int          cnt;
        gchar       *part1;
        gchar       *part2;
        gchar       *date;
        const gchar *currentSel;
        const gchar *split_amount;

        split = splits->data;
        trans = xaccSplitGetParent(split);
        nSplits = xaccTransCountSplits(trans);
        s_list = xaccTransGetSplitList(trans);

        /* Date */
        date = qof_print_date ( xaccTransGetDate(trans));
        part1 = g_strconcat ( end_sep, date, mid_sep, NULL);
        g_free(date);
        /* Name */
        currentSel = xaccAccountGetName(acc);
        part2 = g_strconcat ( part1, currentSel, mid_sep, NULL);
        g_free(part1);
        /* Number */
        currentSel = gnc_get_num_action(trans, NULL);
        part1 = g_strconcat ( part2, currentSel, mid_sep, NULL);
        g_free(part2);
        /* Description */
        currentSel = xaccTransGetDescription(trans);
        part2 = g_strconcat ( part1, currentSel, mid_sep, NULL);
        g_free(part1);
        /* Notes */
        currentSel = xaccTransGetNotes(trans);
        if (currentSel == NULL)
            part1 = g_strconcat ( part2, mid_sep, NULL);
        else
            part1 = g_strconcat ( part2, currentSel, mid_sep, NULL);
        g_free(part2);
        /* Memo */
        currentSel = xaccSplitGetMemo(split);
        part2 = g_strconcat ( part1, currentSel, mid_sep, NULL);
        g_free(part1);
        /* Category */
        currentSel = xaccSplitGetCorrAccountName(split);
        part1 = g_strconcat ( part2, currentSel, mid_sep, "T", mid_sep, NULL);
        g_free(part2);
        /* Action */
        currentSel =  gnc_get_num_action(NULL, split);
        part2 = g_strconcat ( part1, currentSel, mid_sep, NULL);
        g_free(part1);
        /* Reconcile */
        switch (xaccSplitGetReconcile (split))
        {
        case NREC:
            currentSel = "N";
            break;
        case CREC:
            currentSel = "C";
            break;
        case YREC:
            currentSel = "Y";
            break;
        case FREC:
            currentSel = "F";
            break;
        case VREC:
            currentSel = "V";
            break;
        default:
            currentSel = "N";
        }
        part1 = g_strconcat ( part2, currentSel, mid_sep, NULL);
        g_free(part2);
        /* To with Symbol */
        split_amount = xaccPrintAmount(xaccSplitGetAmount(split), gnc_split_amount_print_info(split, TRUE));
        part2 = g_strconcat ( part1, split_amount, mid_sep, NULL);
        g_free(part1);

        /* From with Symbol */
        part1 = g_strconcat ( part2, "", mid_sep, NULL);
        g_free(part2);

        /* To Number Only */
        split_amount = xaccPrintAmount(xaccSplitGetAmount(split), gnc_split_amount_print_info(split, FALSE));
        part2 = g_strconcat ( part1, split_amount, mid_sep, NULL);
        g_free(part1);

        /* From Number Only */
        part1 = g_strconcat ( part2, "", mid_sep, "", mid_sep, "", end_sep, "\n", NULL);
        g_free(part2);

        /* Write to file */
        if (!write_line_to_file(fh, part1))
        {
            info->failed = TRUE;
            break;
        }
        g_free(part1);

        /* Loop through the list of splits for the Transcation */
        node = s_list;
        cnt = 0;
        while ( (cnt < nSplits) && (info->failed == FALSE))
        {
            t_split = node->data;

            /* Start of line */
            part1 = g_strconcat ( end_sep, mid_sep, mid_sep, mid_sep, mid_sep, mid_sep, NULL);

            /* Memo */
            currentSel = xaccSplitGetMemo(t_split);
            part2 = g_strconcat ( part1, currentSel, mid_sep, NULL);
            g_free(part1);

            /* Account */
            currentSel = xaccAccountGetName( xaccSplitGetAccount(t_split));
            part1 = g_strconcat ( part2, currentSel, mid_sep, "S", mid_sep, NULL);
            g_free(part2);

            /* Action */
            currentSel = gnc_get_num_action(NULL, t_split);
            part2 = g_strconcat ( part1, currentSel, mid_sep, NULL);
            g_free(part1);

            /* Reconcile */
            switch (xaccSplitGetReconcile (split))
            {
            case NREC:
                currentSel = "N";
                break;
            case CREC:
                currentSel = "C";
                break;
            case YREC:
                currentSel = "Y";
                break;
            case FREC:
                currentSel = "F";
                break;
            case VREC:
                currentSel = "V";
                break;
            default:
                currentSel = "N";
            }
            part1 = g_strconcat ( part2, currentSel, mid_sep, NULL);
            g_free(part2);

            /* From / To with Symbol */
            split_amount = xaccPrintAmount(xaccSplitGetAmount(t_split), gnc_split_amount_print_info(t_split, TRUE));
            if (xaccSplitGetAccount(t_split) == acc)
                part2 = g_strconcat ( part1,  split_amount, mid_sep, mid_sep, NULL);
            else
                part2 = g_strconcat ( part1, mid_sep, split_amount, mid_sep, NULL);
            g_free(part1);

            /* From / To Numbers only */
            split_amount = xaccPrintAmount(xaccSplitGetAmount(t_split), gnc_split_amount_print_info(t_split, FALSE));
            if (xaccSplitGetAccount(t_split) == acc)
                part1 = g_strconcat ( part2,  split_amount, mid_sep, mid_sep, NULL);
            else
                part1 = g_strconcat ( part2, mid_sep, split_amount, mid_sep, NULL);
            g_free(part2);

            /* From / To - Share Price / Conversion factor */
            split_amount = xaccPrintAmount(xaccSplitGetSharePrice(t_split), gnc_split_amount_print_info(t_split, FALSE));
            if (xaccSplitGetAccount(t_split) == acc)
                part2 = g_strconcat ( part1,  split_amount, mid_sep, end_sep, "\n", NULL);
            else
                part2 = g_strconcat ( part1, mid_sep, split_amount, end_sep, "\n", NULL);
            g_free(part1);

            if (!write_line_to_file(fh, part2))
                info->failed = TRUE;

            g_free(part2);
            cnt++;
            node = node->next;
        }
    }
    g_free(mid_sep);
    qof_query_destroy (q);
    g_list_free( splits );
}
コード例 #16
0
/*******************************************************
 * account_splits
 *
 * gather the splits / transactions for an account and
 * send them to a file
 *******************************************************/
static
void account_splits (CsvExportInfo *info, Account *acc, FILE *fh )
{
    Query   *q;
    GSList  *p1, *p2;
    GList   *splits;
    QofBook *book;

    q = qof_query_create_for (GNC_ID_SPLIT);
    book = gnc_get_current_book();
    qof_query_set_book (q, book);

    /* Sort by transaction date */
    p1 = g_slist_prepend (NULL, TRANS_DATE_POSTED);
    p1 = g_slist_prepend (p1, SPLIT_TRANS);
    p2 = g_slist_prepend (NULL, QUERY_DEFAULT_SORT);
    qof_query_set_sort_order (q, p1, p2, NULL);

    xaccQueryAddSingleAccountMatch (q, acc, QOF_QUERY_AND);
    xaccQueryAddDateMatchTT (q, TRUE, info->csvd.start_time, TRUE, info->csvd.end_time, QOF_QUERY_AND);

    /* Run the query */
    for (splits = qof_query_run (q); splits; splits = splits->next)
    {
        Split       *split;
        Transaction *trans;
        SplitList   *s_list;
        GList       *node;
        Split       *t_split;
        int          nSplits;
        int          cnt;
        gchar       *line;

        split = splits->data;
        trans = xaccSplitGetParent (split);
        nSplits = xaccTransCountSplits (trans);
        s_list = xaccTransGetSplitList (trans);

        // Look for trans already exported in trans_list
        if (g_list_find (info->trans_list, trans) != NULL)
            continue;

        // Simple Layout
        if (info->simple_layout)
        {
            line = make_simple_trans_line (acc, trans, split, info);

            /* Write to file */
            if (!write_line_to_file (fh, line))
            {
                info->failed = TRUE;
                break;
            }
            g_free (line);
            continue;
        }

        // Complex Transaction Line.
        line = make_complex_trans_line (acc, trans, split, info);

        /* Write to file */
        if (!write_line_to_file (fh, line))
        {
            info->failed = TRUE;
            break;
        }
        g_free (line);

        /* Loop through the list of splits for the Transaction */
        node = s_list;
        cnt = 0;
        while ((cnt < nSplits) && (info->failed == FALSE))
        {
            t_split = node->data;

            // Complex Split Line.
            line = make_complex_split_line (trans, t_split, info);

            if (!write_line_to_file (fh, line))
                info->failed = TRUE;

            g_free (line);

            cnt++;
            node = node->next;
        }
        info->trans_list = g_list_prepend (info->trans_list, trans); // add trans to trans_list
    }
    qof_query_destroy (q);
    g_list_free (splits);
}
コード例 #17
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);
    }
}
コード例 #18
0
ファイル: gncOwner.c プロジェクト: ShawnMcGough/gnucash
void
gncOwnerSetLotLinkMemo (Transaction *ll_txn)
{
    gchar *memo_prefix = _("Offset between documents: ");
    gchar *new_memo;
    SplitList *lts_iter;
    SplitList *splits = NULL, *siter;
    GList *titles = NULL, *titer;

    if (!ll_txn)
        return;

    if (xaccTransGetTxnType (ll_txn) != TXN_TYPE_LINK)
        return;

    // Find all splits in the lot link transaction that are also in a document lot
    for (lts_iter = xaccTransGetSplitList (ll_txn); lts_iter; lts_iter = lts_iter->next)
    {
        Split *split = lts_iter->data;
        GNCLot *lot;
        GncInvoice *invoice;
        gchar *title;

        if (!split)
            continue;

        lot = xaccSplitGetLot (split);
        if (!lot)
            continue;

        invoice = gncInvoiceGetInvoiceFromLot (lot);
        if (!invoice)
            continue;

        title = g_strdup_printf ("%s %s", gncInvoiceGetTypeString (invoice), gncInvoiceGetID (invoice));

        titles = g_list_insert_sorted (titles, title, (GCompareFunc)g_strcmp0);
        splits = g_list_prepend (splits, split); // splits don't need to be sorted
    }

    if (!titles)
        return; // We didn't find document lots

    // Create the memo as we'd want it to be
    new_memo = g_strconcat (memo_prefix, titles->data, NULL);
    for (titer = titles->next; titer; titer = titer->next)
    {
        gchar *tmp_memo = g_strconcat (new_memo, " - ", titer->data, NULL);
        g_free (new_memo);
        new_memo = tmp_memo;
    }
    g_list_free_full (titles, g_free);

    // Update the memos of all the splits we found previously (if needed)
    for (siter = splits; siter; siter = siter->next)
    {
        if (g_strcmp0 (xaccSplitGetMemo (siter->data), new_memo) != 0)
            xaccSplitSetMemo (siter->data, new_memo);
    }

    g_list_free (splits);
    g_free (new_memo);
}
コード例 #19
0
ファイル: top-level.c プロジェクト: nizarklai/gnucash-1
static gboolean
gnc_html_register_url_cb (const char *location, const char *label,
                          gboolean new_window, GNCURLResult *result)
{
    GncPluginPage *page = NULL;
    GNCSplitReg * gsr   = NULL;
    Split       * split = NULL;
    Account     * account = NULL;
    Transaction * trans;
    GList       * node;
    QofBook     * book = gnc_get_current_book();
    GncGUID       guid;
    QofInstance * entity = NULL;

    g_return_val_if_fail (location != NULL, FALSE);
    g_return_val_if_fail (result != NULL, FALSE);

    result->load_to_stream = FALSE;

    /* href="gnc-register:account=My Bank Account" */
    if (strncmp("account=", location, 8) == 0)
    {
        account = gnc_account_lookup_by_full_name (gnc_get_current_root_account (),
                  location + 8);
    }

    /* href="gnc-register:guid=12345678901234567890123456789012" */
    else if (strncmp ("acct-guid=", location, strlen ("acct-guid=")) == 0)
    {
        if (!validate_type("acct-guid=", location, GNC_ID_ACCOUNT, result, &guid, &entity))
            return FALSE;

        account = GNC_ACCOUNT(entity);
    }

    else if (strncmp ("trans-guid=", location, strlen ("trans-guid=")) == 0)
    {
        if (!validate_type("trans-guid=", location, GNC_ID_TRANS, result, &guid, &entity))
            return FALSE;

        trans = (Transaction *) entity;

        for (node = xaccTransGetSplitList (trans); node; node = node->next)
        {
            split = node->data;
            account = xaccSplitGetAccount(split);
            if (account) break;
        }

        if (!account)
        {
            result->error_message =
                g_strdup_printf (_("Transaction with no Accounts: %s"), location);
            return FALSE;
        }
    }

    else if (strncmp ("split-guid=", location, strlen ("split-guid=")) == 0)
    {
        if (!validate_type("split-guid=", location, GNC_ID_SPLIT, result, &guid, &entity))
            return FALSE;

        split = (Split *) entity;
        account = xaccSplitGetAccount(split);
    }
    else
    {
        result->error_message =
            g_strdup_printf (_("Unsupported entity type: %s"), location);
        return FALSE;
    }

    page = gnc_plugin_page_register_new (account, FALSE);
    gnc_main_window_open_page (NULL, page);
    if (split)
    {
        gsr = gnc_plugin_page_register_get_gsr(page);
        gnc_split_reg_jump_to_split( gsr, split );
    }

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

    creation_data = (SxTxnCreationData*)user_data;

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

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

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

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

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

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

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

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

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

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

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

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

        xaccSplitSetAccount(copying_split, split_acct);

        {
            gnc_numeric credit_num, debit_num, final;
            gint gncn_error;

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

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

            final = gnc_numeric_sub_fixed( debit_num, credit_num );

            gncn_error = gnc_numeric_check(final);
            if (gncn_error != GNC_ERROR_OK)
            {
                GString *err = g_string_new("");
                g_string_printf(err, "error %d in SX [%s] final gnc_numeric value, using 0 instead",
                                gncn_error, xaccSchedXactionGetName(creation_data->instance->parent->sx));
                g_critical("%s", err->str);
                if (creation_data->creation_errors != NULL)
                    *creation_data->creation_errors = g_list_append(*creation_data->creation_errors, err);
                else
                    g_string_free(err, TRUE);
                final = gnc_numeric_zero();
            }
コード例 #21
0
static gint
_get_vars_helper(Transaction *txn, void *var_hash_data)
{
    GHashTable *var_hash = (GHashTable*)var_hash_data;
    GList *split_list;
    kvp_frame *kvpf;
    kvp_value *kvp_val;
    Split *s;
    char *str;
    gnc_commodity *first_cmdty = NULL;

    split_list = xaccTransGetSplitList(txn);
    if (split_list == NULL)
    {
        return 1;
    }

    for ( ; split_list; split_list = split_list->next)
    {
        gnc_commodity *split_cmdty = NULL;
        GncGUID *acct_guid;
        Account *acct;

        s = (Split*)split_list->data;
        kvpf = xaccSplitGetSlots(s);
        kvp_val = kvp_frame_get_slot_path(kvpf,
                                          GNC_SX_ID,
                                          GNC_SX_ACCOUNT,
                                          NULL);
        acct_guid = kvp_value_get_guid(kvp_val);
        acct = xaccAccountLookup(acct_guid, gnc_get_current_book());
        split_cmdty = xaccAccountGetCommodity(acct);
        if (first_cmdty == NULL)
        {
            first_cmdty = split_cmdty;
        }

        if (! gnc_commodity_equal(split_cmdty, first_cmdty))
        {
            GncSxVariable *var;
            GString *var_name;
            const gchar *split_mnemonic, *first_mnemonic;

            var_name = g_string_sized_new(16);
            split_mnemonic = gnc_commodity_get_mnemonic(split_cmdty);
            first_mnemonic = gnc_commodity_get_mnemonic(first_cmdty);
            g_string_printf(var_name, "%s -> %s",
                            split_mnemonic ? split_mnemonic : "(null)",
                            first_mnemonic ? first_mnemonic : "(null)");
            var = gnc_sx_variable_new(g_strdup(var_name->str));
            g_hash_table_insert(var_hash, g_strdup(var->name), var);
            g_string_free(var_name, TRUE);
        }

        // existing... ------------------------------------------
        kvp_val = kvp_frame_get_slot_path(kvpf,
                                          GNC_SX_ID,
                                          GNC_SX_CREDIT_FORMULA,
                                          NULL);
        if (kvp_val != NULL)
        {
            str = kvp_value_get_string(kvp_val);
            if (str && strlen(str) != 0)
            {
                gnc_sx_parse_vars_from_formula(str, var_hash, NULL);
            }
        }

        kvp_val = kvp_frame_get_slot_path(kvpf,
                                          GNC_SX_ID,
                                          GNC_SX_DEBIT_FORMULA,
                                          NULL);
        if (kvp_val != NULL)
        {
            str = kvp_value_get_string(kvp_val);
            if (str && strlen(str) != 0)
            {
                gnc_sx_parse_vars_from_formula(str, var_hash, NULL);
            }
        }
    }

    return 0;
}