2005-11-07 09:45:58 -06:00
|
|
|
/********************************************************************\
|
2006-01-08 11:51:29 -06:00
|
|
|
* qofbook.c -- dataset access (set of books of entities) *
|
2005-11-07 09:45:58 -06:00
|
|
|
* *
|
|
|
|
* This program is free software; you can redistribute it and/or *
|
|
|
|
* modify it under the terms of the GNU General Public License as *
|
|
|
|
* published by the Free Software Foundation; either version 2 of *
|
|
|
|
* the License, or (at your option) any later version. *
|
|
|
|
* *
|
|
|
|
* This program is distributed in the hope that it will be useful, *
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
|
|
* GNU General Public License for more details. *
|
|
|
|
* *
|
|
|
|
* You should have received a copy of the GNU General Public License*
|
|
|
|
* along with this program; if not, contact: *
|
|
|
|
* *
|
|
|
|
* Free Software Foundation Voice: +1-617-542-5942 *
|
2005-11-16 23:35:02 -06:00
|
|
|
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
|
|
|
|
* Boston, MA 02110-1301, USA gnu@gnu.org *
|
2005-11-07 09:45:58 -06:00
|
|
|
\********************************************************************/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* FILE:
|
|
|
|
* qofbook.c
|
|
|
|
*
|
|
|
|
* FUNCTION:
|
2006-01-08 11:51:29 -06:00
|
|
|
* Encapsulate all the information about a QOF dataset.
|
2005-11-07 09:45:58 -06:00
|
|
|
*
|
|
|
|
* HISTORY:
|
|
|
|
* Created by Linas Vepstas December 1998
|
|
|
|
* Copyright (c) 1998-2001,2003 Linas Vepstas <linas@linas.org>
|
|
|
|
* Copyright (c) 2000 Dave Peticolas
|
2007-05-01 20:57:49 -05:00
|
|
|
* Copyright (c) 2007 David Hampton <hampton@employees.org>
|
2005-11-07 09:45:58 -06:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include <glib.h>
|
|
|
|
|
2006-01-08 11:51:29 -06:00
|
|
|
#include "qof.h"
|
2006-02-20 13:54:03 -06:00
|
|
|
#include "qofevent-p.h"
|
2005-11-07 09:45:58 -06:00
|
|
|
#include "qofbackend-p.h"
|
|
|
|
#include "qofbook-p.h"
|
|
|
|
#include "qofid-p.h"
|
|
|
|
#include "qofobject-p.h"
|
2009-12-04 14:26:48 -06:00
|
|
|
#include "qofbookslots.h"
|
2005-11-07 09:45:58 -06:00
|
|
|
|
|
|
|
static QofLogModule log_module = QOF_MOD_ENGINE;
|
|
|
|
|
2007-04-03 17:30:58 -05:00
|
|
|
QOF_GOBJECT_IMPL(qof_book, QofBook, QOF_TYPE_INSTANCE);
|
|
|
|
|
2005-11-07 09:45:58 -06:00
|
|
|
/* ====================================================================== */
|
|
|
|
/* constructor / destructor */
|
|
|
|
|
|
|
|
static void coll_destroy(gpointer col)
|
|
|
|
{
|
2009-09-18 14:40:57 -05:00
|
|
|
qof_collection_destroy((QofCollection *) col);
|
2005-11-07 09:45:58 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
qof_book_init (QofBook *book)
|
|
|
|
{
|
2009-09-18 14:40:57 -05:00
|
|
|
if (!book) return;
|
2005-11-07 09:45:58 -06:00
|
|
|
|
2009-09-18 14:40:57 -05:00
|
|
|
book->hash_of_collections = g_hash_table_new_full(
|
|
|
|
g_str_hash, g_str_equal,
|
|
|
|
(GDestroyNotify)qof_util_string_cache_remove, /* key_destroy_func */
|
|
|
|
coll_destroy); /* value_destroy_func */
|
2005-11-07 09:45:58 -06:00
|
|
|
|
2009-09-18 14:40:57 -05:00
|
|
|
qof_instance_init_data (&book->inst, QOF_ID_BOOK, book);
|
2005-11-07 09:45:58 -06:00
|
|
|
|
2009-09-18 14:40:57 -05:00
|
|
|
book->data_tables = g_hash_table_new (g_str_hash, g_str_equal);
|
|
|
|
book->data_table_finalizers = g_hash_table_new (g_str_hash, g_str_equal);
|
|
|
|
|
|
|
|
book->book_open = 'y';
|
2011-01-14 17:25:27 -06:00
|
|
|
book->read_only = FALSE;
|
2009-09-18 14:40:57 -05:00
|
|
|
book->version = 0;
|
2005-11-07 09:45:58 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
QofBook *
|
|
|
|
qof_book_new (void)
|
|
|
|
{
|
2009-09-18 14:40:57 -05:00
|
|
|
QofBook *book;
|
2005-11-07 09:45:58 -06:00
|
|
|
|
2009-09-18 14:40:57 -05:00
|
|
|
ENTER (" ");
|
|
|
|
book = g_object_new(QOF_TYPE_BOOK, NULL);
|
|
|
|
qof_object_book_begin (book);
|
2005-11-07 09:45:58 -06:00
|
|
|
|
2009-09-18 14:40:57 -05:00
|
|
|
qof_event_gen (&book->inst, QOF_EVENT_CREATE, NULL);
|
|
|
|
LEAVE ("book=%p", book);
|
|
|
|
return book;
|
2005-11-07 09:45:58 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
book_final (gpointer key, gpointer value, gpointer booq)
|
|
|
|
{
|
2009-09-18 14:40:57 -05:00
|
|
|
QofBookFinalCB cb = value;
|
|
|
|
QofBook *book = booq;
|
2005-11-07 09:45:58 -06:00
|
|
|
|
2009-09-18 14:40:57 -05:00
|
|
|
gpointer user_data = g_hash_table_lookup (book->data_tables, key);
|
|
|
|
(*cb) (book, key, user_data);
|
2005-11-07 09:45:58 -06:00
|
|
|
}
|
|
|
|
|
2007-04-04 22:10:26 -05:00
|
|
|
static void
|
|
|
|
qof_book_dispose_real (GObject *bookp)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2007-04-03 17:30:58 -05:00
|
|
|
static void
|
|
|
|
qof_book_finalize_real (GObject *bookp)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2005-11-07 09:45:58 -06:00
|
|
|
void
|
2009-09-18 14:40:57 -05:00
|
|
|
qof_book_destroy (QofBook *book)
|
2005-11-07 09:45:58 -06:00
|
|
|
{
|
2009-09-18 14:40:57 -05:00
|
|
|
GHashTable* cols;
|
2007-04-04 21:44:47 -05:00
|
|
|
|
2009-09-18 14:40:57 -05:00
|
|
|
if (!book) return;
|
|
|
|
ENTER ("book=%p", book);
|
2005-11-07 09:45:58 -06:00
|
|
|
|
2009-09-18 14:40:57 -05:00
|
|
|
book->shutting_down = TRUE;
|
|
|
|
qof_event_force (&book->inst, QOF_EVENT_DESTROY, NULL);
|
2005-11-07 09:45:58 -06:00
|
|
|
|
2009-09-18 14:40:57 -05:00
|
|
|
/* Call the list of finalizers, let them do their thing.
|
|
|
|
* Do this before tearing into the rest of the book.
|
|
|
|
*/
|
|
|
|
g_hash_table_foreach (book->data_table_finalizers, book_final, book);
|
2005-11-07 09:45:58 -06:00
|
|
|
|
2009-09-18 14:40:57 -05:00
|
|
|
qof_object_book_end (book);
|
2005-11-07 09:45:58 -06:00
|
|
|
|
2009-09-18 14:40:57 -05:00
|
|
|
g_hash_table_destroy (book->data_table_finalizers);
|
|
|
|
book->data_table_finalizers = NULL;
|
|
|
|
g_hash_table_destroy (book->data_tables);
|
|
|
|
book->data_tables = NULL;
|
2005-11-07 09:45:58 -06:00
|
|
|
|
2009-09-18 14:40:57 -05:00
|
|
|
/* qof_instance_release (&book->inst); */
|
2005-11-07 09:45:58 -06:00
|
|
|
|
2009-09-18 14:40:57 -05:00
|
|
|
/* Note: we need to save this hashtable until after we remove ourself
|
|
|
|
* from it, otherwise we'll crash in our dispose() function when we
|
|
|
|
* DO remove ourself from the collection but the collection had already
|
|
|
|
* been destroyed.
|
|
|
|
*/
|
|
|
|
cols = book->hash_of_collections;
|
|
|
|
g_object_unref (book);
|
|
|
|
g_hash_table_destroy (cols);
|
2009-12-11 18:15:50 -06:00
|
|
|
/*book->hash_of_collections = NULL;*/
|
2005-11-07 09:45:58 -06:00
|
|
|
|
2009-09-18 14:40:57 -05:00
|
|
|
LEAVE ("book=%p", book);
|
2005-11-07 09:45:58 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ====================================================================== */
|
|
|
|
/* XXX this should probably be calling is_equal callbacks on gncObject */
|
|
|
|
|
|
|
|
gboolean
|
2006-08-17 23:48:18 -05:00
|
|
|
qof_book_equal (const QofBook *book_1, const QofBook *book_2)
|
2005-11-07 09:45:58 -06:00
|
|
|
{
|
2009-09-18 14:40:57 -05:00
|
|
|
if (book_1 == book_2) return TRUE;
|
|
|
|
if (!book_1 || !book_2) return FALSE;
|
|
|
|
return FALSE;
|
2005-11-07 09:45:58 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ====================================================================== */
|
|
|
|
|
|
|
|
gboolean
|
2006-08-17 23:48:18 -05:00
|
|
|
qof_book_not_saved (const QofBook *book)
|
2005-11-07 09:45:58 -06:00
|
|
|
{
|
2009-09-18 14:40:57 -05:00
|
|
|
if (!book) return FALSE;
|
2005-11-07 09:45:58 -06:00
|
|
|
|
2009-09-18 14:40:57 -05:00
|
|
|
return(qof_instance_get_dirty_flag(book) || qof_object_is_dirty(book));
|
2005-11-07 09:45:58 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2006-05-06 01:01:14 -05:00
|
|
|
qof_book_mark_saved (QofBook *book)
|
2005-11-07 09:45:58 -06:00
|
|
|
{
|
2009-09-18 14:40:57 -05:00
|
|
|
gboolean was_dirty;
|
|
|
|
|
|
|
|
if (!book) return;
|
|
|
|
|
|
|
|
was_dirty = qof_instance_get_dirty_flag(book);
|
|
|
|
qof_instance_set_dirty_flag(book, FALSE);
|
|
|
|
book->dirty_time = 0;
|
|
|
|
qof_object_mark_clean (book);
|
|
|
|
if (was_dirty)
|
|
|
|
{
|
|
|
|
if (book->dirty_cb)
|
|
|
|
book->dirty_cb(book, FALSE, book->dirty_data);
|
|
|
|
}
|
2006-05-06 16:09:59 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void qof_book_mark_dirty (QofBook *book)
|
|
|
|
{
|
2009-09-18 14:40:57 -05:00
|
|
|
gboolean was_dirty;
|
|
|
|
|
|
|
|
if (!book) return;
|
|
|
|
|
|
|
|
was_dirty = qof_instance_get_dirty_flag(book);
|
|
|
|
qof_instance_set_dirty_flag(book, TRUE);
|
|
|
|
if (!was_dirty)
|
|
|
|
{
|
|
|
|
book->dirty_time = time(NULL);
|
|
|
|
if (book->dirty_cb)
|
|
|
|
book->dirty_cb(book, TRUE, book->dirty_data);
|
|
|
|
}
|
2005-11-07 09:45:58 -06:00
|
|
|
}
|
|
|
|
|
2006-05-06 01:01:14 -05:00
|
|
|
void
|
2006-08-17 23:48:18 -05:00
|
|
|
qof_book_print_dirty (const QofBook *book)
|
2006-05-06 01:01:14 -05:00
|
|
|
{
|
2009-09-18 14:40:57 -05:00
|
|
|
if (qof_instance_get_dirty_flag(book))
|
|
|
|
printf("book is dirty.\n");
|
|
|
|
qof_book_foreach_collection
|
2006-08-17 23:48:18 -05:00
|
|
|
(book, (QofCollectionForeachCB)qof_collection_print_dirty, NULL);
|
2006-05-06 01:01:14 -05:00
|
|
|
}
|
|
|
|
|
2006-05-06 16:09:59 -05:00
|
|
|
time_t
|
2006-08-17 23:48:18 -05:00
|
|
|
qof_book_get_dirty_time (const QofBook *book)
|
2006-05-06 16:09:59 -05:00
|
|
|
{
|
2009-09-18 14:40:57 -05:00
|
|
|
return book->dirty_time;
|
2006-05-06 16:09:59 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
qof_book_set_dirty_cb(QofBook *book, QofBookDirtyCB cb, gpointer user_data)
|
|
|
|
{
|
2009-09-18 14:40:57 -05:00
|
|
|
if (book->dirty_cb)
|
|
|
|
g_warning("qof_book_set_dirty_cb: Already existing callback %p, will be overwritten by %p\n",
|
|
|
|
book->dirty_cb, cb);
|
|
|
|
book->dirty_data = user_data;
|
|
|
|
book->dirty_cb = cb;
|
2006-05-06 16:09:59 -05:00
|
|
|
}
|
|
|
|
|
2005-11-07 09:45:58 -06:00
|
|
|
/* ====================================================================== */
|
|
|
|
/* getters */
|
|
|
|
|
2009-09-18 14:40:57 -05:00
|
|
|
QofBackend *
|
2006-08-17 23:48:18 -05:00
|
|
|
qof_book_get_backend (const QofBook *book)
|
2005-11-07 09:45:58 -06:00
|
|
|
{
|
2009-09-18 14:40:57 -05:00
|
|
|
if (!book) return NULL;
|
|
|
|
return book->backend;
|
2005-11-07 09:45:58 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
gboolean
|
2006-08-17 23:48:18 -05:00
|
|
|
qof_book_shutting_down (const QofBook *book)
|
2005-11-07 09:45:58 -06:00
|
|
|
{
|
2009-09-18 14:40:57 -05:00
|
|
|
if (!book) return FALSE;
|
|
|
|
return book->shutting_down;
|
2005-11-07 09:45:58 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ====================================================================== */
|
|
|
|
/* setters */
|
|
|
|
|
|
|
|
void
|
|
|
|
qof_book_set_backend (QofBook *book, QofBackend *be)
|
|
|
|
{
|
2009-09-18 14:40:57 -05:00
|
|
|
if (!book) return;
|
|
|
|
ENTER ("book=%p be=%p", book, be);
|
|
|
|
book->backend = be;
|
|
|
|
LEAVE (" ");
|
2005-11-07 09:45:58 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
void qof_book_kvp_changed (QofBook *book)
|
|
|
|
{
|
2010-02-17 23:31:54 -06:00
|
|
|
qof_book_begin_edit(book);
|
2009-09-18 14:40:57 -05:00
|
|
|
qof_book_mark_dirty(book);
|
2010-02-17 23:31:54 -06:00
|
|
|
qof_book_commit_edit(book);
|
2005-11-07 09:45:58 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ====================================================================== */
|
|
|
|
|
2010-03-03 14:47:10 -06:00
|
|
|
KvpFrame *qof_book_get_slots(const QofBook *book)
|
|
|
|
{
|
|
|
|
return qof_instance_get_slots(QOF_INSTANCE(book));
|
|
|
|
}
|
|
|
|
|
2005-11-07 09:45:58 -06:00
|
|
|
/* Store arbitrary pointers in the QofBook for data storage extensibility */
|
|
|
|
/* XXX if data is NULL, we should remove the key from the hash table!
|
|
|
|
*/
|
2009-09-18 14:40:57 -05:00
|
|
|
void
|
2005-11-07 09:45:58 -06:00
|
|
|
qof_book_set_data (QofBook *book, const char *key, gpointer data)
|
|
|
|
{
|
2009-09-18 14:40:57 -05:00
|
|
|
if (!book || !key) return;
|
|
|
|
g_hash_table_insert (book->data_tables, (gpointer)key, data);
|
2005-11-07 09:45:58 -06:00
|
|
|
}
|
|
|
|
|
2009-09-18 14:40:57 -05:00
|
|
|
void
|
2005-11-07 09:45:58 -06:00
|
|
|
qof_book_set_data_fin (QofBook *book, const char *key, gpointer data, QofBookFinalCB cb)
|
|
|
|
{
|
2009-09-18 14:40:57 -05:00
|
|
|
if (!book || !key) return;
|
|
|
|
g_hash_table_insert (book->data_tables, (gpointer)key, data);
|
2005-11-07 09:45:58 -06:00
|
|
|
|
2009-09-18 14:40:57 -05:00
|
|
|
if (!cb) return;
|
|
|
|
g_hash_table_insert (book->data_table_finalizers, (gpointer)key, cb);
|
2005-11-07 09:45:58 -06:00
|
|
|
}
|
|
|
|
|
2009-09-18 14:40:57 -05:00
|
|
|
gpointer
|
2006-08-17 23:48:18 -05:00
|
|
|
qof_book_get_data (const QofBook *book, const char *key)
|
2005-11-07 09:45:58 -06:00
|
|
|
{
|
2009-09-18 14:40:57 -05:00
|
|
|
if (!book || !key) return NULL;
|
|
|
|
return g_hash_table_lookup (book->data_tables, (gpointer)key);
|
2005-11-07 09:45:58 -06:00
|
|
|
}
|
|
|
|
|
2011-01-14 17:25:27 -06:00
|
|
|
/* ====================================================================== */
|
|
|
|
gboolean
|
|
|
|
qof_book_is_readonly(const QofBook *book)
|
|
|
|
{
|
|
|
|
g_return_val_if_fail( book != NULL, TRUE );
|
|
|
|
return book->read_only;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
qof_book_mark_readonly(QofBook *book)
|
|
|
|
{
|
|
|
|
g_return_if_fail( book != NULL );
|
|
|
|
book->read_only = TRUE;
|
|
|
|
}
|
2005-11-07 09:45:58 -06:00
|
|
|
/* ====================================================================== */
|
|
|
|
|
|
|
|
QofCollection *
|
2006-08-17 23:48:18 -05:00
|
|
|
qof_book_get_collection (const QofBook *book, QofIdType entity_type)
|
2005-11-07 09:45:58 -06:00
|
|
|
{
|
2009-09-18 14:40:57 -05:00
|
|
|
QofCollection *col;
|
|
|
|
|
|
|
|
if (!book || !entity_type) return NULL;
|
|
|
|
|
|
|
|
col = g_hash_table_lookup (book->hash_of_collections, entity_type);
|
|
|
|
if (!col)
|
|
|
|
{
|
|
|
|
col = qof_collection_new (entity_type);
|
|
|
|
g_hash_table_insert(
|
|
|
|
book->hash_of_collections,
|
|
|
|
qof_util_string_cache_insert((gpointer) entity_type), col);
|
|
|
|
}
|
|
|
|
return col;
|
2005-11-07 09:45:58 -06:00
|
|
|
}
|
|
|
|
|
2009-09-18 14:40:57 -05:00
|
|
|
struct _iterate
|
|
|
|
{
|
|
|
|
QofCollectionForeachCB fn;
|
|
|
|
gpointer data;
|
2005-11-07 09:45:58 -06:00
|
|
|
};
|
|
|
|
|
2009-09-18 14:40:57 -05:00
|
|
|
static void
|
2005-11-07 09:45:58 -06:00
|
|
|
foreach_cb (gpointer key, gpointer item, gpointer arg)
|
|
|
|
{
|
2009-09-18 14:40:57 -05:00
|
|
|
struct _iterate *iter = arg;
|
|
|
|
QofCollection *col = item;
|
2005-11-07 09:45:58 -06:00
|
|
|
|
2009-09-18 14:40:57 -05:00
|
|
|
iter->fn (col, iter->data);
|
2005-11-07 09:45:58 -06:00
|
|
|
}
|
|
|
|
|
2009-09-18 14:40:57 -05:00
|
|
|
void
|
|
|
|
qof_book_foreach_collection (const QofBook *book,
|
2005-11-07 09:45:58 -06:00
|
|
|
QofCollectionForeachCB cb, gpointer user_data)
|
|
|
|
{
|
2009-09-18 14:40:57 -05:00
|
|
|
struct _iterate iter;
|
2005-11-07 09:45:58 -06:00
|
|
|
|
2009-09-18 14:40:57 -05:00
|
|
|
g_return_if_fail (book);
|
|
|
|
g_return_if_fail (cb);
|
2005-11-07 09:45:58 -06:00
|
|
|
|
2009-09-18 14:40:57 -05:00
|
|
|
iter.fn = cb;
|
|
|
|
iter.data = user_data;
|
2005-11-07 09:45:58 -06:00
|
|
|
|
2009-09-18 14:40:57 -05:00
|
|
|
g_hash_table_foreach (book->hash_of_collections, foreach_cb, &iter);
|
2005-11-07 09:45:58 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ====================================================================== */
|
|
|
|
|
|
|
|
void qof_book_mark_closed (QofBook *book)
|
|
|
|
{
|
2009-09-18 14:40:57 -05:00
|
|
|
if (!book)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
book->book_open = 'n';
|
2005-11-07 09:45:58 -06:00
|
|
|
}
|
|
|
|
|
2006-08-17 23:48:18 -05:00
|
|
|
gchar qof_book_get_open_marker(const QofBook *book)
|
2005-11-07 09:45:58 -06:00
|
|
|
{
|
2009-09-18 14:40:57 -05:00
|
|
|
if (!book)
|
|
|
|
{
|
|
|
|
return 'n';
|
|
|
|
}
|
|
|
|
return book->book_open;
|
2005-11-07 09:45:58 -06:00
|
|
|
}
|
|
|
|
|
2006-08-17 23:48:18 -05:00
|
|
|
gint32 qof_book_get_version (const QofBook *book)
|
2005-11-07 09:45:58 -06:00
|
|
|
{
|
2009-09-18 14:40:57 -05:00
|
|
|
if (!book)
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return book->version;
|
2005-11-07 09:45:58 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
void qof_book_set_version (QofBook *book, gint32 version)
|
|
|
|
{
|
2009-09-18 14:40:57 -05:00
|
|
|
if (!book && version < 0)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
book->version = version;
|
2005-11-07 09:45:58 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
gint64
|
2010-04-19 11:56:40 -05:00
|
|
|
qof_book_get_counter (QofBook *book, const char *counter_name)
|
2005-11-07 09:45:58 -06:00
|
|
|
{
|
2009-09-18 14:40:57 -05:00
|
|
|
KvpFrame *kvp;
|
|
|
|
KvpValue *value;
|
|
|
|
|
|
|
|
if (!book)
|
|
|
|
{
|
|
|
|
PWARN ("No book!!!");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!counter_name || *counter_name == '\0')
|
|
|
|
{
|
|
|
|
PWARN ("Invalid counter name.");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2011-01-10 15:38:43 -06:00
|
|
|
/* Use the KVP in the book */
|
2009-09-18 14:40:57 -05:00
|
|
|
kvp = qof_book_get_slots (book);
|
|
|
|
|
|
|
|
if (!kvp)
|
|
|
|
{
|
|
|
|
PWARN ("Book has no KVP_Frame");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
value = kvp_frame_get_slot_path (kvp, "counters", counter_name, NULL);
|
|
|
|
if (value)
|
|
|
|
{
|
|
|
|
/* found it */
|
2011-01-10 15:38:54 -06:00
|
|
|
return kvp_value_get_gint64 (value);
|
2009-09-18 14:40:57 -05:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* New counter */
|
2011-01-10 15:38:54 -06:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-01-10 15:39:06 -06:00
|
|
|
gchar *
|
|
|
|
qof_book_increment_and_format_counter (QofBook *book, const char *counter_name)
|
2011-01-10 15:38:54 -06:00
|
|
|
{
|
|
|
|
QofBackend *be;
|
|
|
|
KvpFrame *kvp;
|
|
|
|
KvpValue *value;
|
|
|
|
gint64 counter;
|
2011-01-10 15:39:18 -06:00
|
|
|
gchar* format;
|
2011-01-10 15:38:54 -06:00
|
|
|
|
|
|
|
if (!book)
|
|
|
|
{
|
|
|
|
PWARN ("No book!!!");
|
2011-01-10 15:39:06 -06:00
|
|
|
return NULL;
|
2011-01-10 15:38:54 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!counter_name || *counter_name == '\0')
|
|
|
|
{
|
|
|
|
PWARN ("Invalid counter name.");
|
2011-01-10 15:39:06 -06:00
|
|
|
return NULL;
|
2009-09-18 14:40:57 -05:00
|
|
|
}
|
|
|
|
|
2011-01-10 15:38:54 -06:00
|
|
|
/* Get the current counter value from the KVP in the book. */
|
|
|
|
counter = qof_book_get_counter(book, counter_name);
|
|
|
|
|
|
|
|
/* Check if an error occured */
|
|
|
|
if (counter < 0)
|
2011-01-24 08:10:09 -06:00
|
|
|
return NULL;
|
2011-01-10 15:38:54 -06:00
|
|
|
|
|
|
|
/* Increment the counter */
|
2009-09-18 14:40:57 -05:00
|
|
|
counter++;
|
|
|
|
|
2011-01-10 15:38:54 -06:00
|
|
|
/* Get the KVP from the current book */
|
|
|
|
kvp = qof_book_get_slots (book);
|
|
|
|
|
|
|
|
if (!kvp)
|
|
|
|
{
|
2011-01-24 08:10:09 -06:00
|
|
|
PWARN ("Book has no KVP_Frame");
|
|
|
|
return NULL;
|
2011-01-10 15:38:54 -06:00
|
|
|
}
|
|
|
|
|
2009-09-18 14:40:57 -05:00
|
|
|
/* Save off the new counter */
|
2010-04-19 11:56:40 -05:00
|
|
|
qof_book_begin_edit(book);
|
2009-09-18 14:40:57 -05:00
|
|
|
value = kvp_value_new_gint64 (counter);
|
|
|
|
kvp_frame_set_slot_path (kvp, value, "counters", counter_name, NULL);
|
|
|
|
kvp_value_delete (value);
|
2010-04-19 11:56:40 -05:00
|
|
|
qof_book_mark_dirty(book);
|
|
|
|
qof_book_commit_edit(book);
|
2009-09-18 14:40:57 -05:00
|
|
|
|
2011-01-10 15:39:18 -06:00
|
|
|
format = qof_book_get_counter_format(book, counter_name);
|
|
|
|
|
|
|
|
if (!format)
|
|
|
|
{
|
|
|
|
PWARN("Cannot get format for counter");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2011-01-10 15:39:06 -06:00
|
|
|
/* Generate a string version of the counter */
|
2011-01-10 15:39:18 -06:00
|
|
|
return g_strdup_printf(format, counter);
|
|
|
|
}
|
|
|
|
|
|
|
|
gchar *
|
|
|
|
qof_book_get_counter_format(const QofBook *book, const char *counter_name)
|
|
|
|
{
|
|
|
|
KvpFrame *kvp;
|
|
|
|
gchar *format;
|
|
|
|
KvpValue *value;
|
2011-01-10 15:39:29 -06:00
|
|
|
gchar *error;
|
2011-01-10 15:39:18 -06:00
|
|
|
|
|
|
|
if (!book)
|
|
|
|
{
|
|
|
|
PWARN ("No book!!!");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!counter_name || *counter_name == '\0')
|
|
|
|
{
|
|
|
|
PWARN ("Invalid counter name.");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get the KVP from the current book */
|
|
|
|
kvp = qof_book_get_slots (book);
|
|
|
|
|
|
|
|
if (!kvp)
|
|
|
|
{
|
|
|
|
PWARN ("Book has no KVP_Frame");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2011-01-10 15:39:29 -06:00
|
|
|
format = NULL;
|
|
|
|
|
2011-01-10 15:39:18 -06:00
|
|
|
/* Get the format string */
|
|
|
|
value = kvp_frame_get_slot_path (kvp, "counter_formats", counter_name, NULL);
|
|
|
|
if (value)
|
|
|
|
{
|
|
|
|
format = kvp_value_get_string (value);
|
2011-01-10 15:39:29 -06:00
|
|
|
error = qof_book_validate_counter_format(format);
|
|
|
|
if (error != NULL)
|
|
|
|
{
|
2011-01-10 18:15:02 -06:00
|
|
|
PWARN("Invalid counter format string. Format string: '%s' Counter: '%s' Error: '%s')", format, counter_name, error);
|
2011-01-10 15:39:29 -06:00
|
|
|
/* Invalid format string */
|
|
|
|
format = NULL;
|
|
|
|
g_free(error);
|
|
|
|
}
|
2011-01-10 15:39:18 -06:00
|
|
|
}
|
2011-01-10 15:39:29 -06:00
|
|
|
|
|
|
|
/* If no (valid) format string was found, use the default format
|
|
|
|
* string */
|
|
|
|
if (!format)
|
2011-01-10 15:39:18 -06:00
|
|
|
{
|
|
|
|
/* Use the default format */
|
|
|
|
format = "%.6" G_GINT64_FORMAT;
|
|
|
|
}
|
|
|
|
return format;
|
2005-11-07 09:45:58 -06:00
|
|
|
}
|
|
|
|
|
2011-01-10 15:39:29 -06:00
|
|
|
gchar *
|
|
|
|
qof_book_validate_counter_format(const gchar *p)
|
|
|
|
{
|
|
|
|
const gchar *conv_start, *tmp;
|
|
|
|
|
|
|
|
/* Validate a counter format. This is a very simple "parser" that
|
|
|
|
* simply checks for a single gint64 conversion specification,
|
|
|
|
* allowing all modifiers and flags that printf(3) specifies (except
|
|
|
|
* for the * width and precision, which need an extra argument). */
|
|
|
|
|
|
|
|
/* Skip a prefix of any character except % */
|
|
|
|
while (*p)
|
|
|
|
{
|
|
|
|
/* Skip two adjacent percent marks, which are literal percent
|
|
|
|
* marks */
|
|
|
|
if (p[0] == '%' && p[1] == '%')
|
2011-01-24 08:10:09 -06:00
|
|
|
{
|
2011-01-10 15:39:29 -06:00
|
|
|
p += 2;
|
2011-01-24 08:10:09 -06:00
|
|
|
continue;
|
|
|
|
}
|
2011-01-10 15:39:29 -06:00
|
|
|
/* Break on a single percent mark, which is the start of the
|
|
|
|
* conversion specification */
|
|
|
|
if (*p == '%')
|
|
|
|
break;
|
|
|
|
/* Skip all other characters */
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!*p)
|
|
|
|
return g_strdup("Format string ended without any conversion specification");
|
|
|
|
|
|
|
|
/* Store the start of the conversion for error messages */
|
|
|
|
conv_start = p;
|
|
|
|
|
|
|
|
/* Skip the % */
|
|
|
|
p++;
|
|
|
|
|
|
|
|
/* Skip any number of flag characters */
|
|
|
|
while (*p && strchr("#0- +'I", *p)) p++;
|
|
|
|
|
|
|
|
/* Skip any number of field width digits */
|
|
|
|
while (*p && strchr("0123456789", *p)) p++;
|
|
|
|
|
|
|
|
/* A precision specifier always starts with a dot */
|
|
|
|
if (*p && *p == '.')
|
|
|
|
{
|
|
|
|
/* Skip the . */
|
|
|
|
p++;
|
|
|
|
/* Skip any number of precision digits */
|
|
|
|
while (*p && strchr("0123456789", *p)) p++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!*p)
|
|
|
|
return g_strdup_printf("Format string ended during the conversion specification. Conversion seen so far: %s", conv_start);
|
|
|
|
|
|
|
|
/* See if the format string starts with the correct format
|
|
|
|
* specification. */
|
|
|
|
tmp = strstr(p, G_GINT64_FORMAT);
|
|
|
|
if (tmp == NULL)
|
|
|
|
{
|
|
|
|
return g_strdup_printf("Invalid length modifier and/or conversion specifier ('%.2s'), it should be: " G_GINT64_FORMAT, p);
|
2011-01-24 08:10:09 -06:00
|
|
|
}
|
|
|
|
else if (tmp != p)
|
|
|
|
{
|
2011-01-10 15:39:29 -06:00
|
|
|
return g_strdup_printf("Garbage before length modifier and/or conversion specifier: '%*s'", (int)(tmp - p), p);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Skip length modifier / conversion specifier */
|
|
|
|
p += strlen(G_GINT64_FORMAT);
|
|
|
|
|
|
|
|
/* Skip a suffix of any character except % */
|
|
|
|
while (*p)
|
|
|
|
{
|
|
|
|
/* Skip two adjacent percent marks, which are literal percent
|
|
|
|
* marks */
|
|
|
|
if (p[0] == '%' && p[1] == '%')
|
2011-01-24 08:10:09 -06:00
|
|
|
{
|
2011-01-10 15:39:29 -06:00
|
|
|
p += 2;
|
2011-01-24 08:10:09 -06:00
|
|
|
continue;
|
|
|
|
}
|
2011-01-10 15:39:29 -06:00
|
|
|
/* Break on a single percent mark, which is the start of the
|
|
|
|
* conversion specification */
|
|
|
|
if (*p == '%')
|
|
|
|
return g_strdup_printf("Format string contains unescaped %% signs (or multiple conversion specifications) at '%s'", p);
|
|
|
|
/* Skip all other characters */
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If we end up here, the string was valid, so return no error
|
|
|
|
* message */
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
Bug #537476: Implement currency trading accounts optionally, to be enabled per-book.
Patch by Mike Alexander:
This patch implements trading accounts somewhat as described in Peter
Selinger's document at
<http://www.mathstat.dal.ca/~selinger/accounting/gnucash.html>. Although he
describes it as a multiple currency problem, it really applies to any
transactions involving multiple commodities (for example buying or selling a
stock) Hence I've called the trading accounts "commodity exchange accounts"
which seems more descriptive.
In summary these patches add an option to use commodity exchange accounts and
if it is on a transaction must be balanced both in value (in the transaction
currency) and in each commodity or currency used in any split in the
transaction. If a transaction only contains splits in the transaction currency
then this is the same rule as Gnucash has always enforced.
In this patch, the option to use trading accounts has been moved from
Edit->Preferences to File->Properties and is now associated with the active
book instead of being a global option. If you have set the global value on in
a previous version you will need to set it on again in each file for which you
want trading accounts, the previous global setting will be ignored.
A more detailed list of changes follows:
1. Added a "Use commodity exchange accounts" per-book option.
2. Added gnc_monetary and MonetaryList data types.
3. Renamed xaccTransGetImbalance to xaccTransGetImbalanceValue and added a new
xaccTransGetImbalance that returns a MonetaryList. Also added
xaccTransIsBalanced to see if the transaction is balanced without returning a
GList that needs to be freed. It calls both xaccTransGetImbalance and
xaccTransGetImbalanceValue since a transaction may be unbalanced with regard to
either without being unbalanced with regard to the other.
4. Changed gnc_split_register_get_debcred_bg_color to use xaccTransIsBalanced.
5. Changed gnc_split_register_balance_trans to not offer to adjust an existing
split if there imbalances in multiple currencies. Because of bugs in the
register code this is rarely called.
6. Changed importers to use xaccTransGetImbalanceValue to check for imbalance,
assuming that they won't create multiple currency trasactions.
7. Changed xaccTransScrubImbalance to create a balancing split for each
imbalanced commodity in the transaction. Also balances the transaction value.
The commodity balancing splits go into accounts in the hierarchy
Trading:NAMESPACE:COMMODITY. The value balancing splits go into
Imbalance-CURRENCY as before.
8. Changed xaccSplitConvertAmount to use xaccTransIsBalanced instead of
xaccTransGetImbalance.
9. Changed gnc_split_register_get_debcred_entry to sometimes use the split
amount instead of value if using currency accounts.
If the register is a stock register (i.e., shows shares and prices), it uses
the value if the split is in the register commodity (i.e. is for the stock) and
the amount otherwise. It shows the currency symbol unless the commodity is the
default currency.
If the register is not a stock register it always uses the amount and shows the
currency symbol if the split is not in the register commodity.
Also changed it to not return a value for a null split unless the transaction
is unbalanced in exactly one currency. This is what goes in a blank split as
the proposed value.
10. Changed refresh_model_row to use xaccTransGetImbalanceValue to get the
imbalance, assuming that importers don't create transactions in multiple
currencies. Also same change in gnc_import_process_trans_item,
downloaded_transaction_append, and gnc_import_TransInfo_is_balanced.
11. Changed the TRANS_IMBALANCE accessor method in xaccTransRegister to use
xaccTransGetImbalanceValue. As far as I can tell this is only used by the
"pd-balance" query type in gnc_scm2query_term_query_v1() defined in
engine-helpers.c. This query type only tests the result for zero/non-zero.
12. Changed xaccTransGetAccountConvRate to accept any split into the correct
commodity instead of insisting on one into the provided account. Then can use
it in xaccTransScrubImbalance to set the value of the imbalance split from its
amount, however later changed xaccTransScrubImbalance to not use it. Instead
it sets the value for the new split correctly to keep the value of the whole
transaction balanced.
13. Changed the balance sheet report to include a new option to not compute
unrealized gains and losses.
14. Related to 9 above, changed gnc_split_register_auto_calc to not do anything
if given a stock register where the value cell doesn't contain the value.
15. Also related to 9, changed gnc_split_register_save_amount_values to set the
amount and value fields in the split correctly when using trading accounts.
16. Changed the new account and edit account dialogs to allow any commodity or
currency for an income account if using trading accounts. It would be better
to add a new account type for trading accounts, but that's a big deal and I'll
leave that for later after we see whether this set of changes is going to be
accepted or rejected.
17. Change gnc_xfer_dialog_run_exchange_dialog to understand that the new value
is really the split's amount if using trading accounts.
18. Changed xaccSplitGetOtherSplit to ignore trading splits if using commodity
exchange accounts.
git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@18429 57a11ea4-9604-0410-9ed3-97b8803252fd
2009-11-20 14:11:03 -06:00
|
|
|
/* Determine whether this book uses trading accounts */
|
2010-01-31 14:03:23 -06:00
|
|
|
gboolean
|
|
|
|
qof_book_use_trading_accounts (const QofBook *book)
|
Bug #537476: Implement currency trading accounts optionally, to be enabled per-book.
Patch by Mike Alexander:
This patch implements trading accounts somewhat as described in Peter
Selinger's document at
<http://www.mathstat.dal.ca/~selinger/accounting/gnucash.html>. Although he
describes it as a multiple currency problem, it really applies to any
transactions involving multiple commodities (for example buying or selling a
stock) Hence I've called the trading accounts "commodity exchange accounts"
which seems more descriptive.
In summary these patches add an option to use commodity exchange accounts and
if it is on a transaction must be balanced both in value (in the transaction
currency) and in each commodity or currency used in any split in the
transaction. If a transaction only contains splits in the transaction currency
then this is the same rule as Gnucash has always enforced.
In this patch, the option to use trading accounts has been moved from
Edit->Preferences to File->Properties and is now associated with the active
book instead of being a global option. If you have set the global value on in
a previous version you will need to set it on again in each file for which you
want trading accounts, the previous global setting will be ignored.
A more detailed list of changes follows:
1. Added a "Use commodity exchange accounts" per-book option.
2. Added gnc_monetary and MonetaryList data types.
3. Renamed xaccTransGetImbalance to xaccTransGetImbalanceValue and added a new
xaccTransGetImbalance that returns a MonetaryList. Also added
xaccTransIsBalanced to see if the transaction is balanced without returning a
GList that needs to be freed. It calls both xaccTransGetImbalance and
xaccTransGetImbalanceValue since a transaction may be unbalanced with regard to
either without being unbalanced with regard to the other.
4. Changed gnc_split_register_get_debcred_bg_color to use xaccTransIsBalanced.
5. Changed gnc_split_register_balance_trans to not offer to adjust an existing
split if there imbalances in multiple currencies. Because of bugs in the
register code this is rarely called.
6. Changed importers to use xaccTransGetImbalanceValue to check for imbalance,
assuming that they won't create multiple currency trasactions.
7. Changed xaccTransScrubImbalance to create a balancing split for each
imbalanced commodity in the transaction. Also balances the transaction value.
The commodity balancing splits go into accounts in the hierarchy
Trading:NAMESPACE:COMMODITY. The value balancing splits go into
Imbalance-CURRENCY as before.
8. Changed xaccSplitConvertAmount to use xaccTransIsBalanced instead of
xaccTransGetImbalance.
9. Changed gnc_split_register_get_debcred_entry to sometimes use the split
amount instead of value if using currency accounts.
If the register is a stock register (i.e., shows shares and prices), it uses
the value if the split is in the register commodity (i.e. is for the stock) and
the amount otherwise. It shows the currency symbol unless the commodity is the
default currency.
If the register is not a stock register it always uses the amount and shows the
currency symbol if the split is not in the register commodity.
Also changed it to not return a value for a null split unless the transaction
is unbalanced in exactly one currency. This is what goes in a blank split as
the proposed value.
10. Changed refresh_model_row to use xaccTransGetImbalanceValue to get the
imbalance, assuming that importers don't create transactions in multiple
currencies. Also same change in gnc_import_process_trans_item,
downloaded_transaction_append, and gnc_import_TransInfo_is_balanced.
11. Changed the TRANS_IMBALANCE accessor method in xaccTransRegister to use
xaccTransGetImbalanceValue. As far as I can tell this is only used by the
"pd-balance" query type in gnc_scm2query_term_query_v1() defined in
engine-helpers.c. This query type only tests the result for zero/non-zero.
12. Changed xaccTransGetAccountConvRate to accept any split into the correct
commodity instead of insisting on one into the provided account. Then can use
it in xaccTransScrubImbalance to set the value of the imbalance split from its
amount, however later changed xaccTransScrubImbalance to not use it. Instead
it sets the value for the new split correctly to keep the value of the whole
transaction balanced.
13. Changed the balance sheet report to include a new option to not compute
unrealized gains and losses.
14. Related to 9 above, changed gnc_split_register_auto_calc to not do anything
if given a stock register where the value cell doesn't contain the value.
15. Also related to 9, changed gnc_split_register_save_amount_values to set the
amount and value fields in the split correctly when using trading accounts.
16. Changed the new account and edit account dialogs to allow any commodity or
currency for an income account if using trading accounts. It would be better
to add a new account type for trading accounts, but that's a big deal and I'll
leave that for later after we see whether this set of changes is going to be
accepted or rejected.
17. Change gnc_xfer_dialog_run_exchange_dialog to understand that the new value
is really the split's amount if using trading accounts.
18. Changed xaccSplitGetOtherSplit to ignore trading splits if using commodity
exchange accounts.
git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@18429 57a11ea4-9604-0410-9ed3-97b8803252fd
2009-11-20 14:11:03 -06:00
|
|
|
{
|
|
|
|
const char *opt;
|
|
|
|
kvp_value *kvp_val;
|
2009-12-29 14:12:48 -06:00
|
|
|
|
|
|
|
kvp_val = kvp_frame_get_slot_path (qof_book_get_slots (book),
|
2010-05-06 04:01:46 -05:00
|
|
|
KVP_OPTION_PATH,
|
|
|
|
OPTION_SECTION_ACCOUNTS,
|
|
|
|
OPTION_NAME_TRADING_ACCOUNTS,
|
|
|
|
NULL);
|
Bug #537476: Implement currency trading accounts optionally, to be enabled per-book.
Patch by Mike Alexander:
This patch implements trading accounts somewhat as described in Peter
Selinger's document at
<http://www.mathstat.dal.ca/~selinger/accounting/gnucash.html>. Although he
describes it as a multiple currency problem, it really applies to any
transactions involving multiple commodities (for example buying or selling a
stock) Hence I've called the trading accounts "commodity exchange accounts"
which seems more descriptive.
In summary these patches add an option to use commodity exchange accounts and
if it is on a transaction must be balanced both in value (in the transaction
currency) and in each commodity or currency used in any split in the
transaction. If a transaction only contains splits in the transaction currency
then this is the same rule as Gnucash has always enforced.
In this patch, the option to use trading accounts has been moved from
Edit->Preferences to File->Properties and is now associated with the active
book instead of being a global option. If you have set the global value on in
a previous version you will need to set it on again in each file for which you
want trading accounts, the previous global setting will be ignored.
A more detailed list of changes follows:
1. Added a "Use commodity exchange accounts" per-book option.
2. Added gnc_monetary and MonetaryList data types.
3. Renamed xaccTransGetImbalance to xaccTransGetImbalanceValue and added a new
xaccTransGetImbalance that returns a MonetaryList. Also added
xaccTransIsBalanced to see if the transaction is balanced without returning a
GList that needs to be freed. It calls both xaccTransGetImbalance and
xaccTransGetImbalanceValue since a transaction may be unbalanced with regard to
either without being unbalanced with regard to the other.
4. Changed gnc_split_register_get_debcred_bg_color to use xaccTransIsBalanced.
5. Changed gnc_split_register_balance_trans to not offer to adjust an existing
split if there imbalances in multiple currencies. Because of bugs in the
register code this is rarely called.
6. Changed importers to use xaccTransGetImbalanceValue to check for imbalance,
assuming that they won't create multiple currency trasactions.
7. Changed xaccTransScrubImbalance to create a balancing split for each
imbalanced commodity in the transaction. Also balances the transaction value.
The commodity balancing splits go into accounts in the hierarchy
Trading:NAMESPACE:COMMODITY. The value balancing splits go into
Imbalance-CURRENCY as before.
8. Changed xaccSplitConvertAmount to use xaccTransIsBalanced instead of
xaccTransGetImbalance.
9. Changed gnc_split_register_get_debcred_entry to sometimes use the split
amount instead of value if using currency accounts.
If the register is a stock register (i.e., shows shares and prices), it uses
the value if the split is in the register commodity (i.e. is for the stock) and
the amount otherwise. It shows the currency symbol unless the commodity is the
default currency.
If the register is not a stock register it always uses the amount and shows the
currency symbol if the split is not in the register commodity.
Also changed it to not return a value for a null split unless the transaction
is unbalanced in exactly one currency. This is what goes in a blank split as
the proposed value.
10. Changed refresh_model_row to use xaccTransGetImbalanceValue to get the
imbalance, assuming that importers don't create transactions in multiple
currencies. Also same change in gnc_import_process_trans_item,
downloaded_transaction_append, and gnc_import_TransInfo_is_balanced.
11. Changed the TRANS_IMBALANCE accessor method in xaccTransRegister to use
xaccTransGetImbalanceValue. As far as I can tell this is only used by the
"pd-balance" query type in gnc_scm2query_term_query_v1() defined in
engine-helpers.c. This query type only tests the result for zero/non-zero.
12. Changed xaccTransGetAccountConvRate to accept any split into the correct
commodity instead of insisting on one into the provided account. Then can use
it in xaccTransScrubImbalance to set the value of the imbalance split from its
amount, however later changed xaccTransScrubImbalance to not use it. Instead
it sets the value for the new split correctly to keep the value of the whole
transaction balanced.
13. Changed the balance sheet report to include a new option to not compute
unrealized gains and losses.
14. Related to 9 above, changed gnc_split_register_auto_calc to not do anything
if given a stock register where the value cell doesn't contain the value.
15. Also related to 9, changed gnc_split_register_save_amount_values to set the
amount and value fields in the split correctly when using trading accounts.
16. Changed the new account and edit account dialogs to allow any commodity or
currency for an income account if using trading accounts. It would be better
to add a new account type for trading accounts, but that's a big deal and I'll
leave that for later after we see whether this set of changes is going to be
accepted or rejected.
17. Change gnc_xfer_dialog_run_exchange_dialog to understand that the new value
is really the split's amount if using trading accounts.
18. Changed xaccSplitGetOtherSplit to ignore trading splits if using commodity
exchange accounts.
git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@18429 57a11ea4-9604-0410-9ed3-97b8803252fd
2009-11-20 14:11:03 -06:00
|
|
|
if (kvp_val == NULL)
|
|
|
|
return FALSE;
|
2009-12-29 14:12:48 -06:00
|
|
|
|
Bug #537476: Implement currency trading accounts optionally, to be enabled per-book.
Patch by Mike Alexander:
This patch implements trading accounts somewhat as described in Peter
Selinger's document at
<http://www.mathstat.dal.ca/~selinger/accounting/gnucash.html>. Although he
describes it as a multiple currency problem, it really applies to any
transactions involving multiple commodities (for example buying or selling a
stock) Hence I've called the trading accounts "commodity exchange accounts"
which seems more descriptive.
In summary these patches add an option to use commodity exchange accounts and
if it is on a transaction must be balanced both in value (in the transaction
currency) and in each commodity or currency used in any split in the
transaction. If a transaction only contains splits in the transaction currency
then this is the same rule as Gnucash has always enforced.
In this patch, the option to use trading accounts has been moved from
Edit->Preferences to File->Properties and is now associated with the active
book instead of being a global option. If you have set the global value on in
a previous version you will need to set it on again in each file for which you
want trading accounts, the previous global setting will be ignored.
A more detailed list of changes follows:
1. Added a "Use commodity exchange accounts" per-book option.
2. Added gnc_monetary and MonetaryList data types.
3. Renamed xaccTransGetImbalance to xaccTransGetImbalanceValue and added a new
xaccTransGetImbalance that returns a MonetaryList. Also added
xaccTransIsBalanced to see if the transaction is balanced without returning a
GList that needs to be freed. It calls both xaccTransGetImbalance and
xaccTransGetImbalanceValue since a transaction may be unbalanced with regard to
either without being unbalanced with regard to the other.
4. Changed gnc_split_register_get_debcred_bg_color to use xaccTransIsBalanced.
5. Changed gnc_split_register_balance_trans to not offer to adjust an existing
split if there imbalances in multiple currencies. Because of bugs in the
register code this is rarely called.
6. Changed importers to use xaccTransGetImbalanceValue to check for imbalance,
assuming that they won't create multiple currency trasactions.
7. Changed xaccTransScrubImbalance to create a balancing split for each
imbalanced commodity in the transaction. Also balances the transaction value.
The commodity balancing splits go into accounts in the hierarchy
Trading:NAMESPACE:COMMODITY. The value balancing splits go into
Imbalance-CURRENCY as before.
8. Changed xaccSplitConvertAmount to use xaccTransIsBalanced instead of
xaccTransGetImbalance.
9. Changed gnc_split_register_get_debcred_entry to sometimes use the split
amount instead of value if using currency accounts.
If the register is a stock register (i.e., shows shares and prices), it uses
the value if the split is in the register commodity (i.e. is for the stock) and
the amount otherwise. It shows the currency symbol unless the commodity is the
default currency.
If the register is not a stock register it always uses the amount and shows the
currency symbol if the split is not in the register commodity.
Also changed it to not return a value for a null split unless the transaction
is unbalanced in exactly one currency. This is what goes in a blank split as
the proposed value.
10. Changed refresh_model_row to use xaccTransGetImbalanceValue to get the
imbalance, assuming that importers don't create transactions in multiple
currencies. Also same change in gnc_import_process_trans_item,
downloaded_transaction_append, and gnc_import_TransInfo_is_balanced.
11. Changed the TRANS_IMBALANCE accessor method in xaccTransRegister to use
xaccTransGetImbalanceValue. As far as I can tell this is only used by the
"pd-balance" query type in gnc_scm2query_term_query_v1() defined in
engine-helpers.c. This query type only tests the result for zero/non-zero.
12. Changed xaccTransGetAccountConvRate to accept any split into the correct
commodity instead of insisting on one into the provided account. Then can use
it in xaccTransScrubImbalance to set the value of the imbalance split from its
amount, however later changed xaccTransScrubImbalance to not use it. Instead
it sets the value for the new split correctly to keep the value of the whole
transaction balanced.
13. Changed the balance sheet report to include a new option to not compute
unrealized gains and losses.
14. Related to 9 above, changed gnc_split_register_auto_calc to not do anything
if given a stock register where the value cell doesn't contain the value.
15. Also related to 9, changed gnc_split_register_save_amount_values to set the
amount and value fields in the split correctly when using trading accounts.
16. Changed the new account and edit account dialogs to allow any commodity or
currency for an income account if using trading accounts. It would be better
to add a new account type for trading accounts, but that's a big deal and I'll
leave that for later after we see whether this set of changes is going to be
accepted or rejected.
17. Change gnc_xfer_dialog_run_exchange_dialog to understand that the new value
is really the split's amount if using trading accounts.
18. Changed xaccSplitGetOtherSplit to ignore trading splits if using commodity
exchange accounts.
git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@18429 57a11ea4-9604-0410-9ed3-97b8803252fd
2009-11-20 14:11:03 -06:00
|
|
|
opt = kvp_value_get_string (kvp_val);
|
2009-12-29 14:12:48 -06:00
|
|
|
|
Bug #537476: Implement currency trading accounts optionally, to be enabled per-book.
Patch by Mike Alexander:
This patch implements trading accounts somewhat as described in Peter
Selinger's document at
<http://www.mathstat.dal.ca/~selinger/accounting/gnucash.html>. Although he
describes it as a multiple currency problem, it really applies to any
transactions involving multiple commodities (for example buying or selling a
stock) Hence I've called the trading accounts "commodity exchange accounts"
which seems more descriptive.
In summary these patches add an option to use commodity exchange accounts and
if it is on a transaction must be balanced both in value (in the transaction
currency) and in each commodity or currency used in any split in the
transaction. If a transaction only contains splits in the transaction currency
then this is the same rule as Gnucash has always enforced.
In this patch, the option to use trading accounts has been moved from
Edit->Preferences to File->Properties and is now associated with the active
book instead of being a global option. If you have set the global value on in
a previous version you will need to set it on again in each file for which you
want trading accounts, the previous global setting will be ignored.
A more detailed list of changes follows:
1. Added a "Use commodity exchange accounts" per-book option.
2. Added gnc_monetary and MonetaryList data types.
3. Renamed xaccTransGetImbalance to xaccTransGetImbalanceValue and added a new
xaccTransGetImbalance that returns a MonetaryList. Also added
xaccTransIsBalanced to see if the transaction is balanced without returning a
GList that needs to be freed. It calls both xaccTransGetImbalance and
xaccTransGetImbalanceValue since a transaction may be unbalanced with regard to
either without being unbalanced with regard to the other.
4. Changed gnc_split_register_get_debcred_bg_color to use xaccTransIsBalanced.
5. Changed gnc_split_register_balance_trans to not offer to adjust an existing
split if there imbalances in multiple currencies. Because of bugs in the
register code this is rarely called.
6. Changed importers to use xaccTransGetImbalanceValue to check for imbalance,
assuming that they won't create multiple currency trasactions.
7. Changed xaccTransScrubImbalance to create a balancing split for each
imbalanced commodity in the transaction. Also balances the transaction value.
The commodity balancing splits go into accounts in the hierarchy
Trading:NAMESPACE:COMMODITY. The value balancing splits go into
Imbalance-CURRENCY as before.
8. Changed xaccSplitConvertAmount to use xaccTransIsBalanced instead of
xaccTransGetImbalance.
9. Changed gnc_split_register_get_debcred_entry to sometimes use the split
amount instead of value if using currency accounts.
If the register is a stock register (i.e., shows shares and prices), it uses
the value if the split is in the register commodity (i.e. is for the stock) and
the amount otherwise. It shows the currency symbol unless the commodity is the
default currency.
If the register is not a stock register it always uses the amount and shows the
currency symbol if the split is not in the register commodity.
Also changed it to not return a value for a null split unless the transaction
is unbalanced in exactly one currency. This is what goes in a blank split as
the proposed value.
10. Changed refresh_model_row to use xaccTransGetImbalanceValue to get the
imbalance, assuming that importers don't create transactions in multiple
currencies. Also same change in gnc_import_process_trans_item,
downloaded_transaction_append, and gnc_import_TransInfo_is_balanced.
11. Changed the TRANS_IMBALANCE accessor method in xaccTransRegister to use
xaccTransGetImbalanceValue. As far as I can tell this is only used by the
"pd-balance" query type in gnc_scm2query_term_query_v1() defined in
engine-helpers.c. This query type only tests the result for zero/non-zero.
12. Changed xaccTransGetAccountConvRate to accept any split into the correct
commodity instead of insisting on one into the provided account. Then can use
it in xaccTransScrubImbalance to set the value of the imbalance split from its
amount, however later changed xaccTransScrubImbalance to not use it. Instead
it sets the value for the new split correctly to keep the value of the whole
transaction balanced.
13. Changed the balance sheet report to include a new option to not compute
unrealized gains and losses.
14. Related to 9 above, changed gnc_split_register_auto_calc to not do anything
if given a stock register where the value cell doesn't contain the value.
15. Also related to 9, changed gnc_split_register_save_amount_values to set the
amount and value fields in the split correctly when using trading accounts.
16. Changed the new account and edit account dialogs to allow any commodity or
currency for an income account if using trading accounts. It would be better
to add a new account type for trading accounts, but that's a big deal and I'll
leave that for later after we see whether this set of changes is going to be
accepted or rejected.
17. Change gnc_xfer_dialog_run_exchange_dialog to understand that the new value
is really the split's amount if using trading accounts.
18. Changed xaccSplitGetOtherSplit to ignore trading splits if using commodity
exchange accounts.
git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@18429 57a11ea4-9604-0410-9ed3-97b8803252fd
2009-11-20 14:11:03 -06:00
|
|
|
if (opt && opt[0] == 't' && opt[1] == 0)
|
|
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2010-01-31 14:03:23 -06:00
|
|
|
const char*
|
|
|
|
qof_book_get_string_option(const QofBook* book, const char* opt_name)
|
|
|
|
{
|
|
|
|
return kvp_frame_get_string(qof_book_get_slots(book), opt_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
qof_book_set_string_option(QofBook* book, const char* opt_name, const char* opt_val)
|
|
|
|
{
|
|
|
|
qof_book_begin_edit(book);
|
|
|
|
kvp_frame_set_string(qof_book_get_slots(book), opt_name, opt_val);
|
2010-02-17 23:31:54 -06:00
|
|
|
qof_book_mark_dirty(book);
|
2010-01-31 14:03:23 -06:00
|
|
|
qof_book_commit_edit(book);
|
|
|
|
}
|
|
|
|
|
2010-01-30 16:59:37 -06:00
|
|
|
void
|
|
|
|
qof_book_begin_edit (QofBook *book)
|
|
|
|
{
|
2010-02-17 23:31:54 -06:00
|
|
|
qof_begin_edit(&book->inst);
|
2010-01-30 16:59:37 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
static void commit_err (QofInstance *inst, QofBackendError errcode)
|
|
|
|
{
|
2010-02-17 23:31:54 -06:00
|
|
|
PERR ("Failed to commit: %d", errcode);
|
2010-01-30 16:59:37 -06:00
|
|
|
// gnc_engine_signal_commit_error( errcode );
|
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
static void lot_free(QofInstance* inst)
|
|
|
|
{
|
2010-02-17 23:31:54 -06:00
|
|
|
GNCLot* lot = GNC_LOT(inst);
|
2010-01-30 16:59:37 -06:00
|
|
|
|
2010-02-17 23:31:54 -06:00
|
|
|
gnc_lot_free(lot);
|
2010-01-30 16:59:37 -06:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void noop (QofInstance *inst) {}
|
|
|
|
|
|
|
|
void
|
|
|
|
qof_book_commit_edit(QofBook *book)
|
|
|
|
{
|
2010-02-17 23:31:54 -06:00
|
|
|
if (!qof_commit_edit (QOF_INSTANCE(book))) return;
|
|
|
|
qof_commit_edit_part2 (&book->inst, commit_err, noop, noop/*lot_free*/);
|
2010-01-30 16:59:37 -06:00
|
|
|
}
|
|
|
|
|
2005-11-07 09:45:58 -06:00
|
|
|
/* QofObject function implementation and registration */
|
|
|
|
gboolean qof_book_register (void)
|
|
|
|
{
|
2009-09-18 14:40:57 -05:00
|
|
|
static QofParam params[] =
|
|
|
|
{
|
|
|
|
{ QOF_PARAM_GUID, QOF_TYPE_GUID, (QofAccessFunc)qof_entity_get_guid, NULL },
|
|
|
|
{ QOF_PARAM_KVP, QOF_TYPE_KVP, (QofAccessFunc)qof_instance_get_slots, NULL },
|
|
|
|
{ NULL },
|
|
|
|
};
|
2005-11-07 09:45:58 -06:00
|
|
|
|
2009-09-18 14:40:57 -05:00
|
|
|
qof_class_register (QOF_ID_BOOK, NULL, params);
|
2005-11-07 09:45:58 -06:00
|
|
|
|
2009-09-18 14:40:57 -05:00
|
|
|
return TRUE;
|
2005-11-07 09:45:58 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ========================== END OF FILE =============================== */
|