Sync the g2 branch with the gnome2-merge-3 tag.

git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/branches/gnucash-gnome2-dev@9356 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
David Hampton
2003-09-19 05:58:01 +00:00
parent c2545efc73
commit a7fee8db10
197 changed files with 5371 additions and 3566 deletions

View File

@@ -11,7 +11,8 @@ account_DATA = \
acctchrt_kids.gnucash-xea \
acctchrt_otherasset.gnucash-xea \
acctchrt_otherloan.gnucash-xea \
acctchrt_studium.gnucash-xea
acctchrt_studium.gnucash-xea \
acctchrt_skr04.gnucash-xea
EXTRA_DIST = \

View File

@@ -42,14 +42,14 @@
<act:parent type="new">f7b3424631deed453984e6f5c5569669</act:parent>
</gnc:account>
<gnc:account version="2.0.0">
<act:name>Ausgaben</act:name>
<act:name>Aufwendungen</act:name>
<act:id type="new">bc39f4d37eb75353f4f971e80b9d2818</act:id>
<act:type>EXPENSE</act:type>
<act:commodity>
<cmdty:space>ISO4217</cmdty:space>
<cmdty:id>USD</cmdty:id>
</act:commodity>
<act:description>Ausgaben</act:description>
<act:description>Aufwendungen</act:description>
</gnc:account>
<gnc:account version="2.0.0">
<act:name>Fahrtkosten</act:name>

View File

@@ -42,14 +42,14 @@
<act:parent type="new">13df44e66790069c74d563946aa0394e</act:parent>
</gnc:account>
<gnc:account version="2.0.0">
<act:name>Ausgaben</act:name>
<act:name>Aufwendungen</act:name>
<act:id type="new">bc39f4d37eb75353f4f971e80b9d2818</act:id>
<act:type>EXPENSE</act:type>
<act:commodity>
<cmdty:space>ISO4217</cmdty:space>
<cmdty:id>USD</cmdty:id>
</act:commodity>
<act:description>Ausgaben</act:description>
<act:description>Aufwendungen</act:description>
</gnc:account>
<gnc:account version="2.0.0">
<act:name>Fahrtkosten</act:name>

View File

@@ -96,14 +96,14 @@
<act:parent type="new">724c2a1128e49b0b28304d372b19f5a1</act:parent>
</gnc:account>
<gnc:account version="2.0.0">
<act:name>Ausgaben</act:name>
<act:name>Aufwendungen</act:name>
<act:id type="new">bc39f4d37eb75353f4f971e80b9d2818</act:id>
<act:type>EXPENSE</act:type>
<act:commodity>
<cmdty:space>ISO4217</cmdty:space>
<cmdty:id>USD</cmdty:id>
</act:commodity>
<act:description>Ausgaben</act:description>
<act:description>Aufwendungen</act:description>
</gnc:account>
<gnc:account version="2.0.0">
<act:name>Sonstiges</act:name>

View File

@@ -205,14 +205,14 @@
<act:parent type="new">fbbab8b9f9b46ea19754b9861ac5b6f3</act:parent>
</gnc:account>
<gnc:account version="2.0.0">
<act:name>Ausgaben</act:name>
<act:name>Aufwendungen</act:name>
<act:id type="new">bc39f4d37eb75353f4f971e80b9d2818</act:id>
<act:type>EXPENSE</act:type>
<act:commodity>
<cmdty:space>ISO4217</cmdty:space>
<cmdty:id>USD</cmdty:id>
</act:commodity>
<act:description>Ausgaben</act:description>
<act:description>Aufwendungen</act:description>
</gnc:account>
<gnc:account version="2.0.0">
<act:name>Bildung</act:name>

View File

@@ -294,14 +294,14 @@
<act:parent type="new">13df44e66790069c74d563946aa0394e</act:parent>
</gnc:account>
<gnc:account version="2.0.0">
<act:name>Einkommen</act:name>
<act:name>Ertr<EFBFBD>ge</act:name>
<act:id type="new">724c2a1128e49b0b28304d372b19f5a1</act:id>
<act:type>INCOME</act:type>
<act:commodity>
<cmdty:space>ISO4217</cmdty:space>
<cmdty:id>USD</cmdty:id>
</act:commodity>
<act:description>Einkommen</act:description>
<act:description>Ertr<EFBFBD>ge</act:description>
</gnc:account>
<gnc:account version="2.0.0">
<act:name>BAF&#246;G</act:name>
@@ -469,14 +469,14 @@
<act:parent type="new">fbbab8b9f9b46ea19754b9861ac5b6f3</act:parent>
</gnc:account>
<gnc:account version="2.0.0">
<act:name>Ausgaben</act:name>
<act:name>Aufwendungen</act:name>
<act:id type="new">bc39f4d37eb75353f4f971e80b9d2818</act:id>
<act:type>EXPENSE</act:type>
<act:commodity>
<cmdty:space>ISO4217</cmdty:space>
<cmdty:id>USD</cmdty:id>
</act:commodity>
<act:description>Ausgaben</act:description>
<act:description>Aufwendungen</act:description>
</gnc:account>
<gnc:account version="2.0.0">
<act:name>Bildung</act:name>

View File

@@ -74,14 +74,14 @@
<act:parent type="new">13df44e66790069c74d563946aa0394e</act:parent>
</gnc:account>
<gnc:account version="2.0.0">
<act:name>Ausgaben</act:name>
<act:name>Aufwendungen</act:name>
<act:id type="new">bc39f4d37eb75353f4f971e80b9d2818</act:id>
<act:type>EXPENSE</act:type>
<act:commodity>
<cmdty:space>ISO4217</cmdty:space>
<cmdty:id>USD</cmdty:id>
</act:commodity>
<act:description>Ausgaben</act:description>
<act:description>Aufwendungen</act:description>
</gnc:account>
<gnc:account version="2.0.0">
<act:name>Versicherungen</act:name>

View File

@@ -75,14 +75,14 @@
<act:parent type="new">ccb5a03f67534cb0a2e71658684cc963</act:parent>
</gnc:account>
<gnc:account version="2.0.0">
<act:name>Einkommen</act:name>
<act:name>Ertr<EFBFBD>ge</act:name>
<act:id type="new">724c2a1128e49b0b28304d372b19f5a1</act:id>
<act:type>INCOME</act:type>
<act:commodity>
<cmdty:space>ISO4217</cmdty:space>
<cmdty:id>USD</cmdty:id>
</act:commodity>
<act:description>Einkommen</act:description>
<act:description>Ertr<EFBFBD>ge</act:description>
</gnc:account>
<gnc:account version="2.0.0">
<act:name>Zinsen</act:name>
@@ -129,14 +129,14 @@
<act:parent type="new">fbbab8b9f9b46ea19754b9861ac5b6f3</act:parent>
</gnc:account>
<gnc:account version="2.0.0">
<act:name>Ausgaben</act:name>
<act:name>Aufwendungen</act:name>
<act:id type="new">bc39f4d37eb75353f4f971e80b9d2818</act:id>
<act:type>EXPENSE</act:type>
<act:commodity>
<cmdty:space>ISO4217</cmdty:space>
<cmdty:id>USD</cmdty:id>
</act:commodity>
<act:description>Ausgaben</act:description>
<act:description>Aufwendungen</act:description>
</gnc:account>
<gnc:account version="2.0.0">
<act:name>Versicherungen</act:name>

View File

@@ -10,14 +10,14 @@
Kindergeld, Kindergarten
</gnc-act:long-description>
<gnc:account version="2.0.0">
<act:name>Einkommen</act:name>
<act:name>Ertr<EFBFBD>ge</act:name>
<act:id type="new">724c2a1128e49b0b28304d372b19f5a1</act:id>
<act:type>INCOME</act:type>
<act:commodity>
<cmdty:space>ISO4217</cmdty:space>
<cmdty:id>USD</cmdty:id>
</act:commodity>
<act:description>Einkommen</act:description>
<act:description>Ertr<EFBFBD>ge</act:description>
</gnc:account>
<gnc:account version="2.0.0">
<act:name>Gehalt</act:name>
@@ -42,14 +42,14 @@
<act:parent type="new">e49717ff2ec00bf21e657e2d950ac8a9</act:parent>
</gnc:account>
<gnc:account version="2.0.0">
<act:name>Ausgaben</act:name>
<act:name>Aufwendungen</act:name>
<act:id type="new">bc39f4d37eb75353f4f971e80b9d2818</act:id>
<act:type>EXPENSE</act:type>
<act:commodity>
<cmdty:space>ISO4217</cmdty:space>
<cmdty:id>USD</cmdty:id>
</act:commodity>
<act:description>Ausgaben</act:description>
<act:description>Aufwendungen</act:description>
</gnc:account>
<gnc:account version="2.0.0">
<act:name>Kindergarten</act:name>

View File

@@ -10,14 +10,14 @@
BAF<41>G, Studiengeb<65>hren
</gnc-act:long-description>
<gnc:account version="2.0.0">
<act:name>Einkommen</act:name>
<act:name>Ertr<EFBFBD>ge</act:name>
<act:id type="new">724c2a1128e49b0b28304d372b19f5a1</act:id>
<act:type>INCOME</act:type>
<act:commodity>
<cmdty:space>ISO4217</cmdty:space>
<cmdty:id>USD</cmdty:id>
</act:commodity>
<act:description>Einkommen</act:description>
<act:description>Ertr<EFBFBD>ge</act:description>
</gnc:account>
<gnc:account version="2.0.0">
<act:name>BAF&#246;G</act:name>
@@ -31,14 +31,14 @@
<act:parent type="new">724c2a1128e49b0b28304d372b19f5a1</act:parent>
</gnc:account>
<gnc:account version="2.0.0">
<act:name>Ausgaben</act:name>
<act:name>Aufwendungen</act:name>
<act:id type="new">bc39f4d37eb75353f4f971e80b9d2818</act:id>
<act:type>EXPENSE</act:type>
<act:commodity>
<cmdty:space>ISO4217</cmdty:space>
<cmdty:id>USD</cmdty:id>
</act:commodity>
<act:description>Ausgaben</act:description>
<act:description>Aufwendungen</act:description>
</gnc:account>
<gnc:account version="2.0.0">
<act:name>Bildung</act:name>

View File

@@ -682,7 +682,7 @@
<act:parent type="guid">fbbab8b9f9b46ea19754b9861ac5b6f3</act:parent>
</gnc:account>
<gnc:account version="2.0.0">
<act:name>Ausgaben</act:name>
<act:name>Aufwendungen</act:name>
<act:id type="guid">bc39f4d37eb75353f4f971e80b9d2818</act:id>
<act:type>EXPENSE</act:type>
<act:currency>
@@ -690,7 +690,7 @@
<cmdty:id>DEM</cmdty:id>
</act:currency>
<act:currency-scu>100</act:currency-scu>
<act:description>Ausgaben</act:description>
<act:description>Aufwendungen</act:description>
</gnc:account>
<gnc:account version="2.0.0">
<act:name>Bildung</act:name>

View File

@@ -1,5 +1,5 @@
HOWTO: Translating GnuCash
Last update: 20030611
Last update: 2003-07-04
The concept of this document is to give you step-by-step instructions on
how to update (or create if non-existant) language translations for the
@@ -20,7 +20,7 @@ Sections:
8) Translating the .po file
9) Testing and submitting your translations
10) Problems
11) How to translate the GnuCash manual
11) How to translate the GnuCash guide and/or help files
12) How to translate the files containing the new account hierarchies
==============================================================================
@@ -64,7 +64,7 @@ Checkout the current stable branch.
cvs -z3 -d :pserver:cvs@cvs.gnucash.org:/home/cvs/cvsroot checkout \
-rgnucash-1-8-branch gnucash
Checkout the documentation
Checkout the documentation (optional, but recommended)
cvs -z3 -d :pserver:cvs@cvs.gnucash.org:/home/cvs/cvsroot checkout \
gnucash-docs
@@ -218,7 +218,7 @@ for it:
cp gnucash.pot XXXX.po
If your laguage file does exist, update it using the msgmerge program:
If your language file does exist, update it using the msgmerge program:
/usr/bin/msgmerge -o XXXX.po XXXX.po gnucash.pot
@@ -390,16 +390,26 @@ To follow gnucash as it access files,
strace /opt/gnucash-1.8/bin/gnucash
==============================================================================
11) How to translate the GnuCash manual
11) How to translate the GnuCash guide and/or help files
This section describes the actions needed to translate the manual.
1) Create a new directory doc/sgml/<locale> (where <locale> is
something like es, en_GB, or pt_PT).
2) Copy the files from doc/sgml/C into this directory.
3) Recreate the image files in doc/sgml/C/gnucash so that they are
First, you must *have* the gnucash-doc package installed:
1) Checkout the documentation
cvs -z3 -d :pserver:cvs@cvs.gnucash.org:/home/cvs/cvsroot checkout \
gnucash-docs
2) Create a new directory (if it doesn't already exist) in guide/<locale>
where <locale> is something like es, en_GB, or pt_PT.
3) Copy the files from guide/C into this directory.
4) Recreate the image files in guide/C/figures so that they are
appropriate to the locale.
4) Edit all the sgml files and translate for the locale.
5) Edit all the xml files and translate for the locale.
6) Test that your xml file has no syntax errors
xmllint --valid --noout gnucash-guide.xml
To translate the help files, repeat steps 2-5 but replace the "guide"
directory with "help".
==============================================================================
12) How to translate the files containing the new account hierarchies

View File

@@ -278,7 +278,6 @@ src/gnome/druid-hierarchy.c
src/gnome/druid-loan.c
src/gnome/druid-stock-split.c
src/gnome/gnc-network.c
src/gnome/gnc-splash.c
src/gnome/gnc-split-reg.c
src/gnome/gnucash.desktop.in
src/gnome/gnucash.keys.in
@@ -339,6 +338,7 @@ src/gnome-utils/gnc-html.c
src/gnome-utils/gnc-mdi-utils.c
src/gnome-utils/gnc-menu-extensions.c
src/gnome-utils/gnc-query-list.c
src/gnome-utils/gnc-splash.c
src/gnome-utils/gncmod-gnome-utils.c
src/gnome-utils/gtkselect.c
src/gnome-utils/print-session.c

View File

@@ -1,7 +1,8 @@
EXTRA_DIST = \
.cvsignore \
gnucash.spec.in
gnucash.spec.in \
gnucash.spec
all-local: gnucash.spec
@@ -9,10 +10,10 @@ all-local: gnucash.spec
## brackets here, instead of the usual @...@. This prevents autoconf
## from substituting the values directly into the left-hand sides of
## the sed substitutions. *sigh*
gnucash.spec: gnucash.spec.in Makefile
gnucash.spec: gnucash.spec.in Makefile ${top_builddir}/config.status
rm -f $@.tmp
sed < $< > $@.tmp \
-e 's:@-VERSION-@:${VERSION}:'
mv $@.tmp $@
CLEANFILES = gnucash.spec
DISTCLEANFILES = gnucash.spec

View File

@@ -21,3 +21,43 @@ wait a bit, and a new binary rpm will be in
/usr/src/redhat/SRPMS.
================================
JPL version:
1) Obtain a GnuCash distribution (source code .tar.gz file),
either by downloading from an ftp site somewhere, or building
from CVS using "make dist". The file should have the nomenclature
gnucash-X.Y.Z.tar.gz where X.Y.Z are the release numbers.
2) Copy and uncompress/tar the distribution in your
/usr/src/redhat/SOURCES directory. You will need to do this as the
root user. Note, use your distribution-specific path here, only
redhat uses "/usr/src/redhat".
cd /usr/src/redhat/SOURCES
cp /SOMEPATH/gnucash-X.Y.Z.tar.gz
tar xvzf gnucash-X.Y.Z.tar.gz
3) Copy the rpm spec file to /usr/src/redhat/SPEC.
cp gnucash-X.Y.Z/rpm/gnucash.spec /usr/src/redhat/SPECS/.
4) Edit the spec file, you probably will only need to modify the
following 3 lines, depending on what features you want to include
in the rpm:
%define _with_postgres 0 (use postgres backend?)
%define _with_ofx 0 (use openofx package?)
%define _with_hbci 0 (use hbci package?)
5) Build the binary and source RPMs
cd /usr/src/redhat/SPECS
rpmbuild -ba gnucash.spec
(note: older versions of rpm use "rpm -ba FILE.spec" to build)
6) Wait a bit, and a new binary rpm will be in
/usr/src/redhat/RPMS/<arch> and a new src.rpm will be in
/usr/src/redhat/SRPMS.

View File

@@ -45,4 +45,5 @@ bin_SCRIPTS = gnc-test-env
EXTRA_DIST = \
.cvsignore \
README.modules \
gnc-test-env
gnc-test-env \
valgrind-gnucash.supp

View File

@@ -1,4 +1,5 @@
SUBDIRS = gnome . test
PWD := $(shell pwd)
pkglib_LTLIBRARIES = libgncmod-app-file.la libgw-app-file.la

View File

@@ -815,6 +815,10 @@ gnc_file_save_as (void)
qof_session_destroy (session);
session = NULL;
/* XXX At this point, we should really mark the data in the new session
* as being 'dirty', since we haven't saved it at all under the new
* session. But I'm lazy...
*/
qof_session_set_current_session(new_session);
/* --------------- END CORE SESSION CODE -------------- */

View File

@@ -1,4 +1,5 @@
SUBDIRS = . test
PWD := $(shell pwd)
pkglib_LTLIBRARIES = libgncmod-app-utils.la libgw-app-utils.la

View File

@@ -102,8 +102,8 @@ gncReadFile (const char * file, char ** data)
if( fd == -1 )
{
ERROR();
PERR ("file was %s\n", file);
int norr = errno;
PERR ("file %s: (%d) %s \n", file, norr, strerror(norr));
return 0;
}

View File

@@ -3,7 +3,6 @@
;;; rudimentary "class" system for straight Scheme
;;;
;;; Bill Gribble <grib@billgribble.com> 20 Feb 2000
;;; $Id$
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; this is an extremely rudimentary object system. Each object is a

View File

@@ -64,12 +64,23 @@ run_tests (void)
val2str = scm_c_eval_string ("gnc:value->string");
g_return_if_fail (SCM_PROCEDUREP (val2str));
for (i = 0; i < 100; i++) {
for (i = 0; i < 244; i++) {
q = get_random_query ();
test_query (q, val2str);
xaccFreeQuery (q);
printf("%d ", i);
fflush(stdout);
}
success ("");
{
q = get_random_query ();
test_query (q, val2str);
xaccFreeQuery (q);
printf("%d ", i);
fflush(stdout);
}
printf("\n");
}
static void
@@ -86,6 +97,10 @@ main_helper (void *closure, int argc, char **argv)
/* double->string->double is not idempotent */
kvp_exclude_type (KVP_TYPE_DOUBLE);
/* Initialize to a known RNG position */
guid_init();
srand(1);
run_tests ();
print_test_results ();

View File

@@ -37,6 +37,7 @@ libgncmod_backend_file_la_SOURCES = \
sixtp.c
noinst_HEADERS = \
gnc-backend-file.h \
gnc-xml.h \
gnc-xml-helper.h \
io-example-account.h \

View File

@@ -191,7 +191,7 @@ struct account_pdata
QofBook *book;
};
static gboolean
static inline gboolean
set_string(xmlNodePtr node, Account* act,
void (*func)(Account *act, const gchar *txt))
{
@@ -296,12 +296,6 @@ depricated_account_currency_handler (xmlNodePtr node, gpointer act_pdata)
static gboolean
depricated_account_currency_scu_handler (xmlNodePtr node, gpointer act_pdata)
{
struct account_pdata *pdata = act_pdata;
gint64 val;
dom_tree_to_integer(node, &val);
DxaccAccountSetCurrencySCU(pdata->account, val);
return TRUE;
}
@@ -312,7 +306,7 @@ depricated_account_security_handler (xmlNodePtr node, gpointer act_pdata)
gnc_commodity *ref;
ref = dom_tree_to_commodity_ref_no_engine(node, pdata->book);
DxaccAccountSetSecurity(pdata->account, ref);
xaccAccountSetCommodity(pdata->account, ref);
return TRUE;
}

View File

@@ -1,8 +1,32 @@
/*********************************************************************
* gnc-backend-file.c: load and save data to files
/********************************************************************
* gnc-backend-file.c: load and save data to files *
* *
* 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 *
* 59 Temple Place - Suite 330 Fax: +1-617-542-2652 *
* Boston, MA 02111-1307, USA gnu@gnu.org *
\********************************************************************/
/** @file gnc-backend-file.c
* @breif load and save data to files
* @author Copyright (c) 2000 Gnumatic Inc.
* @author Copyright (c) 2002 Derek Atkins <warlord@MIT.EDU>
* @author Copyright (c) 2003 Linas Vepstas <linas@linas.org>
*
*
*********************************************************************/
* This file implements the top-level QofBackend API for saving/
* restoring data to/from an ordinary Unix filesystem file.
*/
#define _GNU_SOURCE
@@ -17,46 +41,24 @@
#include <dirent.h>
#include <time.h>
#include "Group.h"
#include "TransLog.h"
#include "gnc-engine.h"
#include "gnc-date.h"
#include "gnc-trace.h"
#include "gnc-engine-util.h"
#include "gnc-pricedb-p.h"
#include "io-gncxml.h"
#include "io-gncbin.h"
#include "io-gncxml-v2.h"
#include "gnc-backend-api.h"
#include "gnc-engine.h"
#include "gnc-engine-util.h"
#include "gnc-backend-file.h"
#include "qofbackend-p.h"
#include "qofbook-p.h"
#include "qofsession.h"
static short module = MOD_IO;
struct FileBackend_struct
{
QofBackend be;
char *dirname;
char *fullpath;
char *lockfile;
char *linkfile;
int lockfd;
QofSession *session;
};
typedef struct FileBackend_struct FileBackend;
typedef enum
{
GNC_BOOK_NOT_OURS,
GNC_BOOK_BIN_FILE,
GNC_BOOK_XML1_FILE,
GNC_BOOK_XML2_FILE,
} QofBookFileType;
static short module = MOD_BACKEND;
static int file_retention_days = 0;
static gboolean file_compression = FALSE;
@@ -64,10 +66,11 @@ static gboolean file_compression = FALSE;
static void gnc_file_be_load_from_file(QofBackend *, QofBook *);
static gboolean gnc_file_be_get_file_lock (FileBackend *be);
static gboolean gnc_file_be_write_to_file(FileBackend *be,
static gboolean gnc_file_be_write_to_file(FileBackend *be, QofBook *,
const gchar *filepath,
gboolean make_backup);
static void gnc_file_be_write_accounts_to_file(QofBackend *be,
QofBook *book);
QofBook *book);
static void gnc_file_be_remove_old_files(FileBackend *be);
QofBackend * libgncmod_backend_file_LTX_gnc_backend_new(void);
@@ -84,21 +87,19 @@ gnc_file_be_set_compression (gboolean compress)
file_compression = compress;
}
/* ================================================================= */
static void
file_session_begin(QofBackend *be_start, QofSession *session, const char *book_id,
file_session_begin(QofBackend *be_start, QofSession *session,
const char *book_id,
gboolean ignore_lock, gboolean create_if_nonexistent)
{
FileBackend* be;
FileBackend *be = (FileBackend*) be_start;
char *p;
ENTER (" ");
be = (FileBackend*) be_start;
be->session = session;
/* Make sure the directory is there */
be->dirname = g_strdup (qof_session_get_file_path (session));
be->fullpath = g_strdup (be->dirname);
p = strrchr (be->dirname, '/');
@@ -108,7 +109,6 @@ file_session_begin(QofBackend *be_start, QofSession *session, const char *book_i
int rc;
*p = '\0';
rc = stat (be->dirname, &statbuf);
if (rc != 0 || !S_ISDIR(statbuf.st_mode))
{
@@ -143,13 +143,13 @@ file_session_begin(QofBackend *be_start, QofSession *session, const char *book_i
return;
}
/* ================================================================= */
static void
file_session_end(QofBackend *be_start)
{
FileBackend* be;
be = (FileBackend*)be_start;
FileBackend *be = (FileBackend*)be_start;
ENTER (" ");
if (be->linkfile)
unlink (be->linkfile);
@@ -171,6 +171,7 @@ file_session_end(QofBackend *be_start)
g_free (be->linkfile);
be->linkfile = NULL;
LEAVE (" ");
}
static void
@@ -182,10 +183,112 @@ file_destroy_backend(QofBackend *be)
static void
file_sync_all(QofBackend* be, QofBook *book)
{
gnc_file_be_write_to_file((FileBackend*)be, TRUE);
gnc_file_be_remove_old_files((FileBackend*)be);
FileBackend *fbe = (FileBackend *) be;
ENTER ("book=%p, primary=%p", book, fbe->primary_book);
/* We make an important assumption here, that we might want to change
* in the future: when the user says 'save', we really save the one,
* the only, the current open book, and nothing else. We do this
* because we assume that any other books that we are dealing with
* are 'read-only', non-editable, because they are closed books.
* If we ever want to have more than one book open read-write,
* this will have to change.
*/
if (NULL == fbe->primary_book) fbe->primary_book = book;
if (book != fbe->primary_book) return;
gnc_file_be_write_to_file (fbe, book, fbe->fullpath, TRUE);
gnc_file_be_remove_old_files (fbe);
LEAVE ("book=%p", book);
}
/* ================================================================= */
/* Routines to deal with the creation of multiple books.
* The core design assumption here is that the book
* begin-edit/commit-edit routines are used solely to write out
* closed accounting periods to files. They're not currently
* designed to do anything other than this. (Although they could be).
*/
static char *
build_period_filepath (FileBackend *fbe, QofBook *book)
{
int len;
char *str, *p, *q;
len = strlen (fbe->fullpath) + GUID_ENCODING_LENGTH + 14;
str = g_new (char, len);
strcpy (str, fbe->fullpath);
/* XXX it would be nice for the user if we made the book
* closing date and/or title part of the file-name. */
p = strrchr (str, '/');
p++;
p = stpcpy (p, "book-");
p = guid_to_string_buff (qof_book_get_guid(book), p);
p = stpcpy (p, "-");
q = strrchr (fbe->fullpath, '/');
q++;
p = stpcpy (p, q);
p = stpcpy (p, ".gml");
return str;
}
static void
file_begin_edit (QofBackend *be, QofIdTypeConst typ, gpointer gp)
{
FileBackend *fbe = (FileBackend *) be;
QofBook *book = gp;
const char * filepath;
if (strcmp (GNC_ID_PERIOD, typ)) return;
filepath = build_period_filepath(fbe, book);
PINFO (" ====================== book=%p filepath=%s\n", book, filepath);
if (NULL == fbe->primary_book)
{
PERR ("You should have saved the data "
"at least once before closing the books!\n");
}
/* XXX To be anal about it, we should really be checking to see
* if there already is a file with this book GUID, and disallowing
* further progress. This is because we are not allowed to
* modify books that are closed (They should be treated as
* 'read-only').
*/
}
static void
file_rollback_edit (QofBackend *be, QofIdTypeConst typ, gpointer gp)
{
QofBook *book = gp;
if (strcmp (GNC_ID_PERIOD, typ)) return;
PINFO ("book=%p", book);
}
static void
file_commit_edit (QofBackend *be, QofIdTypeConst typ, gpointer gp)
{
FileBackend *fbe = (FileBackend *) be;
QofBook *book = gp;
const char * filepath;
if (strcmp (GNC_ID_PERIOD, typ)) return;
filepath = build_period_filepath(fbe, book);
PINFO (" ====================== book=%p filepath=%s\n", book, filepath);
gnc_file_be_write_to_file(fbe, book, filepath, FALSE);
/* We want to force a save of the current book at this point,
* because if we don't, and the user forgets to do so, then
* there'll be the same transactions in the closed book,
* and also in the current book. */
gnc_file_be_write_to_file (fbe, fbe->primary_book, fbe->fullpath, TRUE);
}
/* ================================================================= */
QofBackend *
libgncmod_backend_file_LTX_gnc_backend_new(void)
{
@@ -202,14 +305,12 @@ libgncmod_backend_file_LTX_gnc_backend_new(void)
be->load = gnc_file_be_load_from_file;
/* The file backend will never have transactional
* behaviour. So these vectors are null. */
/* The file backend treats accounting periods transactionally. */
be->begin = file_begin_edit;
be->commit = file_commit_edit;
be->rollback = file_rollback_edit;
be->begin = NULL;
be->commit = NULL;
be->rollback = NULL;
/* the file backend always loads all data ... */
/* The file backend always loads all data ... */
be->compile_query = NULL;
be->free_query = NULL;
be->run_query = NULL;
@@ -217,7 +318,7 @@ libgncmod_backend_file_LTX_gnc_backend_new(void)
be->counter = NULL;
/* the file backend will never be multi-user... */
/* The file backend will never be multi-user... */
be->events_pending = NULL;
be->process_events = NULL;
@@ -230,12 +331,12 @@ libgncmod_backend_file_LTX_gnc_backend_new(void)
fbe->linkfile = NULL;
fbe->lockfd = -1;
fbe->session = NULL;
fbe->primary_book = NULL;
return be;
}
/* ---------------------------------------------------------------------- */
/* ================================================================= */
static gboolean
gnc_file_be_get_file_lock (FileBackend *be)
@@ -259,15 +360,15 @@ gnc_file_be_get_file_lock (FileBackend *be)
{
/* oops .. we can't create the lockfile .. */
switch (errno) {
case EACCES:
case EROFS:
case ENOSPC:
be_err = ERR_BACKEND_READONLY;
break;
default:
be_err = ERR_BACKEND_LOCKED;
break;
}
case EACCES:
case EROFS:
case ENOSPC:
be_err = ERR_BACKEND_READONLY;
break;
default:
be_err = ERR_BACKEND_LOCKED;
break;
}
qof_backend_set_error ((QofBackend*)be, be_err);
return FALSE;
}
@@ -389,21 +490,23 @@ gnc_file_be_load_from_file (QofBackend *bend, QofBook *book)
gboolean rc;
FileBackend *be = (FileBackend *) bend;
be->primary_book = book;
switch (gnc_file_be_determine_file_type(be->fullpath))
{
case GNC_BOOK_XML2_FILE:
rc = qof_session_load_from_xml_file_v2 (be->session);
rc = qof_session_load_from_xml_file_v2 (be, book);
if (FALSE == rc) error = ERR_FILEIO_PARSE_ERROR;
break;
case GNC_BOOK_XML1_FILE:
rc = qof_session_load_from_xml_file (be->session);
rc = qof_session_load_from_xml_file (book, be->fullpath);
if (FALSE == rc) error = ERR_FILEIO_PARSE_ERROR;
break;
case GNC_BOOK_BIN_FILE:
/* presume it's an old-style binary file */
qof_session_load_from_binfile(be->session);
qof_session_load_from_binfile(book, be->fullpath);
error = gnc_get_binfile_io_error();
break;
@@ -414,11 +517,15 @@ gnc_file_be_load_from_file (QofBackend *bend, QofBook *book)
}
if(error != ERR_BACKEND_NO_ERR)
{
qof_backend_set_error(bend, error);
}
/* We just got done loading, it can't possibly be dirty !! */
qof_book_mark_saved (book);
}
/* ---------------------------------------------------------------------- */
/* Write the financial data in a book to a file, returning FALSE on
error and setting the error_result to indicate what went wrong if
it's not NULL. This function does not manage file locks in any
@@ -433,7 +540,7 @@ static gboolean
copy_file(const char *orig, const char *bkup)
{
static int buf_size = 1024;
char buf[buf_size];
char buf[buf_size];
int orig_fd;
int bkup_fd;
ssize_t count_write;
@@ -556,8 +663,8 @@ gnc_file_be_select_files (const struct dirent *d)
return(0);
return((strcmp(d->d_name + len, ".LNK") == 0) ||
(strcmp(d->d_name + len, ".xac") == 0) ||
(strcmp(d->d_name + len, ".log") == 0));
(strcmp(d->d_name + len, ".xac") == 0) ||
(strcmp(d->d_name + len, ".log") == 0));
}
static void
@@ -600,42 +707,50 @@ gnc_file_be_remove_old_files(FileBackend *be)
char *name;
int len;
if (gnc_file_be_select_files (dent) == 0)
continue;
if (gnc_file_be_select_files (dent) == 0)
continue;
name = g_strconcat(be->dirname, "/", dent->d_name, NULL);
len = strlen(name) - 4;
name = g_strconcat(be->dirname, "/", dent->d_name, NULL);
len = strlen(name) - 4;
/* Is this file associated with the current data file */
if (strncmp(name, be->fullpath, pathlen) == 0) {
if (strncmp(name, be->fullpath, pathlen) == 0)
{
if ((safe_strcmp(name + len, ".LNK") == 0) &&
/* Is a lock file. Skip the active lock file */
/* Is a lock file. Skip the active lock file */
(safe_strcmp(name, be->linkfile) != 0) &&
/* Only delete lock files older than the active one */
(stat(name, &statbuf) == 0) &&
(statbuf.st_mtime <lockstatbuf.st_mtime)) {
unlink(name);
} else if (file_retention_days > 0) {
time_t file_time;
struct tm file_tm;
int days;
const char* res;
(statbuf.st_mtime <lockstatbuf.st_mtime))
{
PINFO ("unlink lock file: %s", name);
unlink(name);
}
else if (file_retention_days > 0)
{
time_t file_time;
struct tm file_tm;
int days;
const char* res;
PINFO ("file retention = %d days", file_retention_days);
/* Is the backup file old enough to delete */
memset(&file_tm, 0, sizeof(file_tm));
res = strptime(name+pathlen+1, "%Y%m%d%H%M%S", &file_tm);
file_time = mktime(&file_tm);
days = (int)(difftime(now, file_time) / 86400);
file_time = mktime(&file_tm);
days = (int)(difftime(now, file_time) / 86400);
/* Make sure this file actually has a date before unlinking */
if (res && res != name+pathlen+1 &&
/* We consumed some but not all of the filename */
file_time > 0 &&
/* we actually have a reasonable time and it is old enough */
days > file_retention_days) {
unlink(name);
}
/* Make sure this file actually has a date before unlinking */
if (res && res != name+pathlen+1 &&
/* We consumed some but not all of the filename */
file_time > 0 &&
/* we actually have a reasonable time and it is old enough */
days > file_retention_days)
{
PINFO ("unlink stale (%d days old) file: %s", days, name);
unlink(name);
}
}
}
g_free(name);
@@ -643,20 +758,25 @@ gnc_file_be_remove_old_files(FileBackend *be)
closedir (dir);
}
/* ---------------------------------------------------------------------- */
static gboolean
gnc_file_be_write_to_file(FileBackend *be, gboolean make_backup)
gnc_file_be_write_to_file(FileBackend *fbe,
QofBook *book,
const gchar *datafile,
gboolean make_backup)
{
const gchar *datafile;
QofBackend *be = &fbe->be;
char *tmp_name;
QofBook *book;
struct stat statbuf;
int rc;
QofBackendError be_err;
book = qof_session_get_book (be->session);
ENTER (" book=%p file=%s", book, datafile);
datafile = be->fullpath;
/* If the book is 'clean', recently saved, then don't save again. */
/* XXX this is currently broken due to faulty 'Save As' logic. */
/* if (FALSE == qof_book_not_saved (book)) return FALSE; */
tmp_name = g_new(char, strlen(datafile) + 12);
strcpy(tmp_name, datafile);
@@ -664,13 +784,13 @@ gnc_file_be_write_to_file(FileBackend *be, gboolean make_backup)
if(!mktemp(tmp_name))
{
qof_backend_set_error((QofBackend*)be, ERR_BACKEND_MISC);
qof_backend_set_error(be, ERR_BACKEND_MISC);
return FALSE;
}
if(make_backup)
{
if(!gnc_file_be_backup_file(be))
if(!gnc_file_be_backup_file(fbe))
{
return FALSE;
}
@@ -685,20 +805,22 @@ gnc_file_be_write_to_file(FileBackend *be, gboolean make_backup)
/* Use the permissions from the original data file */
if(chmod(tmp_name, statbuf.st_mode) != 0)
{
qof_backend_set_error(be, ERR_BACKEND_PERM);
PWARN("unable to chmod filename %s: %s",
datafile ? datafile : "(null)",
strerror(errno) ? strerror(errno) : "");
#if VFAT_DOESNT_SUCK
#if VFAT_DOESNT_SUCK /* chmod always fails on vfat fs */
g_free(tmp_name);
return FALSE;
#endif
}
if(chown(tmp_name, statbuf.st_uid, statbuf.st_gid) != 0)
{
qof_backend_set_error(be, ERR_BACKEND_PERM);
PWARN("unable to chown filename %s: %s",
datafile ? datafile : "(null)",
strerror(errno) ? strerror(errno) : "");
#if VFAT_DOESNT_SUCK
#if VFAT_DOESNT_SUCK /* chown always fails on vfat fs */
g_free(tmp_name);
return FALSE;
#endif
@@ -706,21 +828,22 @@ gnc_file_be_write_to_file(FileBackend *be, gboolean make_backup)
}
if(unlink(datafile) != 0 && errno != ENOENT)
{
qof_backend_set_error((QofBackend*)be, ERR_BACKEND_MISC);
qof_backend_set_error(be, ERR_FILEIO_BACKUP_ERROR);
PWARN("unable to unlink filename %s: %s",
datafile ? datafile : "(null)",
strerror(errno) ? strerror(errno) : "");
g_free(tmp_name);
return FALSE;
}
if(!gnc_int_link_or_make_backup(be, tmp_name, datafile))
if(!gnc_int_link_or_make_backup(fbe, tmp_name, datafile))
{
qof_backend_set_error(be, ERR_FILEIO_BACKUP_ERROR);
g_free(tmp_name);
return FALSE;
}
if(unlink(tmp_name) != 0)
{
qof_backend_set_error((QofBackend*)be, ERR_BACKEND_MISC);
qof_backend_set_error(be, ERR_BACKEND_PERM);
PWARN("unable to unlink temp filename %s: %s",
tmp_name ? tmp_name : "(null)",
strerror(errno) ? strerror(errno) : "");
@@ -728,23 +851,28 @@ gnc_file_be_write_to_file(FileBackend *be, gboolean make_backup)
return FALSE;
}
g_free(tmp_name);
/* Since we successfully saved the book,
* we should mark it clean. */
qof_book_mark_saved (book);
LEAVE (" sucessful save of book=%p to file=%s", book, datafile);
return TRUE;
}
else
{
if(unlink(tmp_name) != 0)
{
switch (errno) {
case ENOENT: /* tmp_name doesn't exist? Assume "RO" error */
case EACCES:
case EPERM:
case EROFS:
be_err = ERR_BACKEND_READONLY;
break;
default:
be_err = ERR_BACKEND_MISC;
}
qof_backend_set_error((QofBackend*)be, be_err);
switch (errno) {
case ENOENT: /* tmp_name doesn't exist? Assume "RO" error */
case EACCES:
case EPERM:
case EROFS:
be_err = ERR_BACKEND_READONLY;
break;
default:
be_err = ERR_BACKEND_MISC;
}
qof_backend_set_error(be, be_err);
PWARN("unable to unlink temp_filename %s: %s",
tmp_name ? tmp_name : "(null)",
strerror(errno) ? strerror(errno) : "");
@@ -753,6 +881,7 @@ gnc_file_be_write_to_file(FileBackend *be, gboolean make_backup)
g_free(tmp_name);
return FALSE;
}
return TRUE;
}
static void
@@ -764,3 +893,4 @@ gnc_file_be_write_accounts_to_file(QofBackend *be, QofBook *book)
gnc_book_write_accounts_to_xml_file_v2(be, book, datafile);
}
/* ========================== END OF FILE ===================== */

View File

@@ -88,7 +88,7 @@ append_group (xmlNodePtr parent, AccountGroup *grp)
}
}
static gboolean
static int
traverse_txns (Transaction *txn, gpointer data)
{
xmlNodePtr node;
@@ -97,7 +97,7 @@ traverse_txns (Transaction *txn, gpointer data)
node = gnc_transaction_dom_tree_create(txn);
xmlAddChild (parent, node);
return TRUE;
return 0;
}
#endif

View File

@@ -106,10 +106,9 @@ price_parse_xml_sub_node(GNCPrice *p, xmlNodePtr sub_node, QofBook *book)
if(!c) return FALSE;
gnc_price_set_currency(p, c);
} else if(safe_strcmp("price:time", sub_node->name) == 0) {
Timespec *t = dom_tree_to_timespec(sub_node);
if(!t) return FALSE;
gnc_price_set_time(p, *t);
g_free(t);
Timespec t = dom_tree_to_timespec(sub_node);
if(!is_valid_timespec(t)) return FALSE;
gnc_price_set_time(p, t);
} else if(safe_strcmp("price:source", sub_node->name) == 0) {
char *text = dom_tree_to_text(sub_node);
if(!text) return FALSE;

View File

@@ -202,7 +202,7 @@ struct split_pdata
QofBook *book;
};
static gboolean
static inline gboolean
set_spl_string(xmlNodePtr node, Split *spl,
void (*func)(Split *spl, const char *txt))
{
@@ -216,7 +216,7 @@ set_spl_string(xmlNodePtr node, Split *spl,
return TRUE;
}
static gboolean
static inline gboolean
set_spl_gnc_num(xmlNodePtr node, Split* spl,
void (*func)(Split *spl, gnc_numeric gn))
{
@@ -275,14 +275,12 @@ static gboolean
spl_reconcile_date_handler(xmlNodePtr node, gpointer data)
{
struct split_pdata *pdata = data;
Timespec *ts;
Timespec ts;
ts = dom_tree_to_timespec(node);
g_return_val_if_fail(ts, FALSE);
g_return_val_if_fail(is_valid_timespec(ts), FALSE);
xaccSplitSetDateReconciledTS(pdata->split, ts);
g_free(ts);
xaccSplitSetDateReconciledTS(pdata->split, &ts);
return TRUE;
}
@@ -415,7 +413,7 @@ struct trans_pdata
QofBook *book;
};
static gboolean
static inline gboolean
set_tran_string(xmlNodePtr node, Transaction *trn,
void (*func)(Transaction *trn, const char *txt))
{
@@ -432,24 +430,22 @@ set_tran_string(xmlNodePtr node, Transaction *trn,
return TRUE;
}
static gboolean
static inline gboolean
set_tran_date(xmlNodePtr node, Transaction *trn,
void (*func)(Transaction *trn, const Timespec *tm))
{
Timespec *tm;
Timespec tm;
tm = dom_tree_to_timespec(node);
g_return_val_if_fail(tm, FALSE);
g_return_val_if_fail(is_valid_timespec(tm), FALSE);
func(trn, tm);
g_free(tm);
func(trn, &tm);
return TRUE;
}
static gboolean
static inline gboolean
trn_id_handler(xmlNodePtr node, gpointer trans_pdata)
{
struct trans_pdata *pdata = trans_pdata;

View File

@@ -133,13 +133,6 @@ add_account_local(GncExampleAccount *gea, Account *act)
table = gnc_book_get_commodity_table (gea->book);
clear_up_account_commodity(table, act,
DxaccAccountGetCurrency,
DxaccAccountSetCurrency);
clear_up_account_commodity(table, act,
DxaccAccountGetSecurity,
DxaccAccountSetSecurity);
clear_up_account_commodity(table, act,
xaccAccountGetCommodity,
xaccAccountSetCommodity);

View File

@@ -1,9 +1,5 @@
/********************************************************************\
* io-gncbin.c -- read and write (old format) binary datafile *
* (GnuCash/X-Accountant) *
* Copyright (C) 1997 Robin D. Clark *
* Copyright (C) 1997-2001 Linas Vepstas <linas@linas.org> *
* Copyright (C) 1999-2000 Rob Browning *
* io-gncbin-r.c -- read (old X-Accountant format) binary datafile *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
@@ -23,6 +19,12 @@
* Boston, MA 02111-1307, USA gnu@gnu.org *
* *
********************************************************************
* @file io-gncbin-r.c
* @breif read (old X-Accountant format) binary datafile
* @author Copyright (C) 1997 Robin D. Clark
* @author Copyright (C) 1997-2001 Linas Vepstas <linas@linas.org>
* @author Copyright (C) 1999-2000 Rob Browning
*
* NOTE: the readxxxx/writexxxx functions changed the current *
* position in the file, and so the order which these *
* functions are called in important *
@@ -226,7 +228,7 @@ cvt_potential_prices_to_pricedb_and_cleanup(GNCPriceDB **prices,
gnc_price_set_commodity(price,
DxaccAccountGetSecurity(split_acct));
gnc_price_set_currency(price,
DxaccAccountGetCurrency(split_acct));
xaccTransGetCurrency(txn));
gnc_price_set_time(price, time);
gnc_price_set_source(price, "old-file-import");
gnc_price_set_type(price, "unknown");
@@ -536,11 +538,10 @@ gnc_load_financials_from_fd(QofBook *book, int fd)
* Return: the struct with the program data in it *
\********************************************************************/
void
qof_session_load_from_binfile(QofSession *session)
qof_session_load_from_binfile(QofBook *book, const char * datafile)
{
int fd;
const gchar *datafile = qof_session_get_file_path(session);
if(!datafile) {
error_code = ERR_BACKEND_MISC;
return;
@@ -555,8 +556,7 @@ qof_session_load_from_binfile(QofSession *session)
return;
}
if (!gnc_load_financials_from_fd(qof_session_get_book(session), fd))
return;
gnc_load_financials_from_fd(book, fd);
close(fd);
}
@@ -617,7 +617,7 @@ readGroup (QofBook *book, int fd, Account *aparent, int token)
* readAccount *
* reads in the data for an account from the datafile *
* *
* Args: book - the session *
* Args: book - the top-level account object *
* fd - the filedescriptor of the data file *
* acc - the account structure to be filled in *
* token - the datafile version *

View File

@@ -1,9 +1,5 @@
/********************************************************************\
* io-gncbin.h -- read and write (old format) binary datafile *
* (X-Accountant) *
* Copyright (C) 1997 Robin D. Clark *
* Copyright (C) 1998, 1999 Linas Vepstas *
* *
* io-gncbin.h -- read (old X-Accountant format) binary datafile *
* 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 *
@@ -31,11 +27,13 @@
#define IO_GNCBIN_H
#include "qofbackend.h"
#include "qofsession.h"
#include "qofbook.h"
/** PROTOTYPES ******************************************************/
/*
/* @file io-gncbin.h
* @breif read (old X-Accountant format) binary datafile
* @author Copyright (C) 1997 Robin D. Clark
* @author Copyright (C) 1998, 1999 Linas Vepstas <linas@linas.org>
*
* NOTE: These routines should not be used directly for file IO. They
* are not inherently safe against file-locking errors. For direct
* file IO, the gnc-book's higher level functions should be used.
@@ -49,7 +47,7 @@
* thought of as a "stack of depth one", and this routine as a "pop".
* Future implementations may have a deeper stack.
*/
void qof_session_load_from_binfile(QofSession *session);
void qof_session_load_from_binfile(QofBook *, const char * filepath);
QofBackendError gnc_get_binfile_io_error(void);
#endif /* IO_GNCBIN_H */

View File

@@ -359,23 +359,15 @@ gncxml_setup_for_read (GNCParseStatus *global_parse_status)
/* ================================================================== */
gboolean
qof_session_load_from_xml_file(QofSession *session)
qof_session_load_from_xml_file(QofBook *book, const char *filename)
{
gboolean parse_ok;
gpointer parse_result = NULL;
sixtp *top_level_pr;
GNCParseStatus global_parse_status;
const gchar *filename;
QofBook *book;
g_return_val_if_fail(session, FALSE);
book = qof_session_get_book (session);
global_parse_status.book = book;
g_return_val_if_fail(book, FALSE);
filename = qof_session_get_file_path(session);
g_return_val_if_fail(filename, FALSE);
top_level_pr = gncxml_setup_for_read (&global_parse_status);
@@ -3593,10 +3585,9 @@ price_parse_xml_sub_node(GNCPrice *p, xmlNodePtr sub_node, QofBook *book)
if(!c) return FALSE;
gnc_price_set_currency(p, c);
} else if(safe_strcmp("price:time", sub_node->name) == 0) {
Timespec *t = dom_tree_to_timespec(sub_node);
if(!t) return FALSE;
gnc_price_set_time(p, *t);
g_free(t);
Timespec t = dom_tree_to_timespec(sub_node);
if(!is_valid_timespec(t)) return FALSE;
gnc_price_set_time(p, t);
} else if(safe_strcmp("price:source", sub_node->name) == 0) {
char *text = dom_tree_to_text(sub_node);
if(!text) return FALSE;

View File

@@ -41,7 +41,6 @@
#include "qofbackend-p.h"
#include "qofbook.h"
#include "qofbook-p.h"
#include "qofsession.h"
#include "sixtp-dom-parsers.h"
#include "io-gncxml-v2.h"
@@ -58,17 +57,17 @@ static short module = MOD_IO;
/* Callback structure */
struct file_backend {
gboolean ok;
gpointer data;
sixtp_gdv2 * gd;
const char * tag;
sixtp * parser;
FILE * out;
QofBook * book;
gboolean ok;
gpointer data;
sixtp_gdv2 * gd;
const char * tag;
sixtp * parser;
FILE * out;
QofBook * book;
};
#define GNC_V2_STRING "gnc-v2"
extern const gchar *gnc_v2_book_version_string; /* see gnc-book-xml-v2 */
extern const gchar *gnc_v2_book_version_string; /* see gnc-book-xml-v2 */
void
run_callback(sixtp_gdv2 *data, const char *type)
@@ -169,19 +168,13 @@ add_account_local(sixtp_gdv2 *data, Account *act)
clear_up_account_commodity(table, act,
DxaccAccountGetCurrency,
DxaccAccountSetCurrency,
DxaccAccountGetCurrencySCU,
DxaccAccountSetCurrencySCU);
clear_up_account_commodity(table, act,
DxaccAccountGetSecurity,
DxaccAccountSetSecurity,
NULL, NULL);
clear_up_account_commodity(table, act,
xaccAccountGetCommodity,
xaccAccountSetCommodity,
xaccAccountGetCommoditySCUi,
xaccAccountSetCommoditySCUandFlag);
xaccAccountSetCommoditySCU);
xaccAccountScrubCommodity (act);
@@ -409,9 +402,9 @@ gnc_counter_end_handler(gpointer data_for_children,
if (be_data.ok == FALSE)
{
PERR("Unknown type: %s", type ? type : "(null)");
xmlFree (type);
return FALSE;
PERR("Unknown type: %s", type ? type : "(null)");
xmlFree (type);
return FALSE;
}
}
@@ -465,15 +458,15 @@ file_rw_feedback (sixtp_gdv2 *gd, const char *type)
percentage = (loaded * 100)/total;
if (percentage > 100) {
printf("Transactions: Total: %d, Loaded: %d\n",
counter->transactions_total, counter->transactions_loaded);
counter->transactions_total, counter->transactions_loaded);
printf("Accounts: Total: %d, Loaded: %d\n",
counter->accounts_total, counter->accounts_loaded);
counter->accounts_total, counter->accounts_loaded);
printf("Books: Total: %d, Loaded: %d\n",
counter->books_total, counter->books_loaded);
counter->books_total, counter->books_loaded);
printf("Commodities: Total: %d, Loaded: %d\n",
counter->commodities_total, counter->commodities_loaded);
counter->commodities_total, counter->commodities_loaded);
printf("Scheduled Tansactions: Total: %d, Loaded: %d\n",
counter->schedXactions_total, counter->schedXactions_loaded);
counter->schedXactions_total, counter->schedXactions_loaded);
}
percentage = MIN(percentage, 100);
gd->gui_display_fn(NULL, percentage);
@@ -591,8 +584,8 @@ add_parser_cb (const char *type, gpointer data_p, gpointer be_data_p)
if (data->create_parser)
if(!sixtp_add_some_sub_parsers(
be_data->parser, TRUE,
data->type_name, (data->create_parser)(),
NULL, NULL))
data->type_name, (data->create_parser)(),
NULL, NULL))
be_data->ok = FALSE;
}
@@ -640,19 +633,16 @@ gnc_sixtp_gdv2_new (
}
gboolean
qof_session_load_from_xml_file_v2(QofSession *session)
qof_session_load_from_xml_file_v2(FileBackend *fbe, QofBook *book)
{
QofBook *book;
AccountGroup *grp;
QofBackend *be;
AccountGroup *grp;
QofBackend *be = &fbe->be;
sixtp_gdv2 *gd;
sixtp *top_parser;
sixtp *main_parser;
sixtp *book_parser;
struct file_backend be_data;
book = qof_session_get_book (session);
be = (QofBackend *)qof_book_get_backend(book);
gd = gnc_sixtp_gdv2_new(book, FALSE, file_rw_feedback, be->percentage);
top_parser = sixtp_new();
@@ -711,7 +701,7 @@ qof_session_load_from_xml_file_v2(QofSession *session)
/* stop logging while we load */
xaccLogDisable ();
if(!gnc_xml_parse_file(top_parser, qof_session_get_file_path(session),
if(!gnc_xml_parse_file(top_parser, fbe->fullpath,
generic_callback, gd, book))
{
sixtp_destroy(top_parser);
@@ -727,8 +717,8 @@ qof_session_load_from_xml_file_v2(QofSession *session)
be_data.book = book;
gncObjectForeachBackend (GNC_FILE_BACKEND, scrub_cb, &be_data);
grp = gnc_book_get_group(book);
/* fix price quote sources */
grp = gnc_book_get_group(book);
xaccGroupScrubQuoteSources (grp, gnc_book_get_commodity_table(book));
/* Fix account and transaction commodities */
@@ -831,8 +821,8 @@ write_counts_cb (const char *type, gpointer data_p, gpointer be_data_p)
if (data->get_count)
write_counts (be_data->out, data->type_name,
(data->get_count) (be_data->book),
NULL);
(data->get_count) (be_data->book),
NULL);
}
static void
@@ -915,10 +905,14 @@ write_commodities(FILE *out, QofBook *book, sixtp_gdv2 *gd)
tbl = gnc_book_get_commodity_table(book);
namespaces = g_list_sort(gnc_commodity_table_get_namespaces(tbl),
compare_namespaces);
namespaces = gnc_commodity_table_get_namespaces(tbl);
if(namespaces)
{
namespaces = g_list_sort(namespaces, compare_namespaces);
}
for(lp = namespaces; lp; lp = lp->next) {
for(lp = namespaces; lp; lp = lp->next)
{
gchar *space;
if(!lp->data) {
@@ -927,13 +921,15 @@ write_commodities(FILE *out, QofBook *book, sixtp_gdv2 *gd)
}
space = (gchar *) lp->data;
if(!gnc_commodity_namespace_is_iso(space)) {
if(!gnc_commodity_namespace_is_iso(space))
{
GList *comms = gnc_commodity_table_get_commodities(tbl, space);
GList *lp2;
comms = g_list_sort(comms, compare_commodity_ids);
for(lp2 = comms; lp2; lp2 = lp2->next) {
for(lp2 = comms; lp2; lp2 = lp2->next)
{
xmlNodePtr comnode = gnc_commodity_dom_tree_create(
(gnc_commodity *) lp2->data);
@@ -941,15 +937,15 @@ write_commodities(FILE *out, QofBook *book, sixtp_gdv2 *gd)
fprintf(out, "\n");
xmlFreeNode(comnode);
gd->counter.commodities_loaded++;
run_callback(gd, "commodities");
gd->counter.commodities_loaded++;
run_callback(gd, "commodities");
}
g_list_free (comms);
}
}
g_list_free (namespaces);
if (namespaces) g_list_free (namespaces);
}
static void
@@ -970,7 +966,7 @@ write_pricedb(FILE *out, QofBook *book, sixtp_gdv2 *gd)
xmlFreeNode(node);
}
static gboolean
static int
xml_add_trn_data(Transaction *t, gpointer data)
{
struct file_backend *be_data = data;
@@ -984,7 +980,7 @@ xml_add_trn_data(Transaction *t, gpointer data)
xmlFreeNode(node);
be_data->gd->counter.transactions_loaded++;
run_callback(be_data->gd, "transaction");
return TRUE;
return 0;
}
static void
@@ -1037,8 +1033,8 @@ write_schedXactions( FILE *out, QofBook *book, sixtp_gdv2 *gd)
xmlElemDump( out, NULL, node );
fprintf( out, "\n" );
xmlFreeNode( node );
gd->counter.schedXactions_loaded++;
run_callback(gd, "schedXactions");
gd->counter.schedXactions_loaded++;
run_callback(gd, "schedXactions");
} while ( (schedXactions = schedXactions->next) );
}
@@ -1083,7 +1079,7 @@ gnc_book_write_to_xml_filehandle_v2(QofBook *book, FILE *out)
"book", 1,
NULL);
be = (QofBackend *)qof_book_get_backend(book);
be = qof_book_get_backend(book);
gd = gnc_sixtp_gdv2_new(book, FALSE, file_rw_feedback, be->percentage);
gd->counter.commodities_total =
gnc_commodity_table_get_size(gnc_book_get_commodity_table(book));
@@ -1114,7 +1110,7 @@ gnc_book_write_accounts_to_xml_filehandle_v2(QofBackend *be, QofBook *book, FILE
if (!out) return FALSE;
grp = gnc_book_get_group(book);
grp = gnc_book_get_group(book);
nacc = 1 + xaccGroupGetNumSubAccounts(grp);
table = gnc_book_get_commodity_table(book);

View File

@@ -23,9 +23,10 @@
\********************************************************************/
/*
* io-gncxml.h -- api for new XML-based file format
* @file io-gncxml.h
* @breif api for GnuCash version 2 XML-based file format
*
* Initial code by James LewisMoss
* @author Initial code by James LewisMoss, 2001
*/
#ifndef IO_GNCXML_V2_H
@@ -33,16 +34,9 @@
#include <glib.h>
#include "Account.h"
#include "Transaction.h"
#include "gnc-commodity.h"
#include "gnc-engine.h"
#include "gnc-pricedb.h"
#include "SchedXaction.h"
#include "qofbook.h"
#include "qofsession.h"
#include "qofbackend.h"
#include "gnc-backend-file.h"
#include "gnc-engine.h"
#include "sixtp.h"
@@ -107,7 +101,7 @@ typedef struct
sixtp * (*create_parser) (void);
gboolean (*add_item)(sixtp_gdv2 *, gpointer obj);
int (*get_count) (QofBook *);
int (*get_count) (QofBook *);
void (*write) (FILE*, QofBook*);
void (*scrub) (QofBook *);
} GncXmlDataType_t;
@@ -122,14 +116,14 @@ typedef struct
{
AccountList *accts;
TransList *transactions;
QofBook *book;
QofBook *book;
} gnc_template_xaction_data;
/* Call after loading each record */
void run_callback(sixtp_gdv2 *data, const char *type);
/* read in an account group from a file */
gboolean qof_session_load_from_xml_file_v2(QofSession *session);
gboolean qof_session_load_from_xml_file_v2(FileBackend *, QofBook *);
/* write all book info to a file */
gboolean gnc_book_write_to_xml_filehandle_v2(QofBook *book, FILE *fh);

View File

@@ -23,9 +23,10 @@
\********************************************************************/
/*
* io-gncxml.h -- api for new XML-based file format
* @file io-gncxml.h
* @breif api for Version 1 XML-based file format
*
* Initial code by Rob l. Browning 4Q 2000
* Initial code by Rob L. Browning 4Q 2000
* Tuneups by James LewisMoss Dec 2000
*/
@@ -33,17 +34,15 @@
#define IO_GNCXML_H
#include <glib.h>
#include "qofsession.h"
#include "Query.h"
#include "qofbook.h"
/* FIXME: eventually, we probably need to add an error stack
accessable via gnc_book_get_xml_io_error() a la binfile. */
/* read in an account group from a file */
gboolean qof_session_load_from_xml_file(QofSession *session);
/** Read in an account group from a file */
gboolean qof_session_load_from_xml_file(QofBook *, const char * filename);
/* The is_gncxml_file() routine checks to see if the first few
/** The is_gncxml_file() routine checks to see if the first few
* chars of the file look like gnc-xml data.
*/
gboolean gnc_is_xml_data_file(const gchar *name);

View File

@@ -47,7 +47,7 @@ dom_tree_to_guid(xmlNodePtr node)
if(strcmp(node->properties->name, "type") != 0)
{
PERR("Unknown attribute for id tag: %s\n",
PERR("Unknown attribute for id tag: %s",
node->properties->name ?
(char *) node->properties->name : "(null)");
return NULL;
@@ -189,15 +189,14 @@ dom_tree_to_guid_kvp_value(xmlNodePtr node)
kvp_value*
dom_tree_to_timespec_kvp_value (xmlNodePtr node)
{
Timespec * ts;
Timespec ts;
kvp_value * ret = NULL;
ts = dom_tree_to_timespec (node);
if (ts)
ret = kvp_value_new_timespec (*ts);
g_free (ts);
if (ts.tv_sec || ts.tv_nsec)
{
ret = kvp_value_new_timespec (ts);
}
return ret;
}
@@ -464,7 +463,7 @@ dom_tree_to_text(xmlNodePtr tree)
return NULL;
}
PINFO ("node string is >>>%s<<<\n", temp);
PINFO ("node string is >>>%s<<<", temp);
result = g_strdup (temp);
xmlFree (temp);
return result;
@@ -493,18 +492,16 @@ dom_tree_to_gnc_numeric(xmlNodePtr node)
}
}
static Timespec*
timespec_failure(Timespec *would_have_been)
static inline Timespec
timespec_failure(Timespec ts)
{
if(would_have_been)
{
g_free(would_have_been);
}
return NULL;
ts.tv_sec = 0;
ts.tv_nsec = 0;
return ts;
}
Timespec*
Timespec
dom_tree_to_timespec(xmlNodePtr node)
{
/* Turn something like this
@@ -518,15 +515,14 @@ dom_tree_to_timespec(xmlNodePtr node)
undefined. The XML is valid if it has at least one of <s> or <ns>
and no more than one of each. Order is irrelevant. */
Timespec *ret;
Timespec ret;
gboolean seen_s = FALSE;
gboolean seen_ns = FALSE;
xmlNodePtr n;
ret = g_new(Timespec, 1);
ret->tv_sec = 0;
ret->tv_nsec = 0;
ret.tv_sec = 0;
ret.tv_nsec = 0;
for(n = node->xmlChildrenNode; n; n = n->next) {
switch(n->type) {
case XML_COMMENT_NODE:
@@ -546,7 +542,7 @@ dom_tree_to_timespec(xmlNodePtr node)
return timespec_failure(ret);
}
if(!string_to_timespec_secs(content, ret)) {
if(!string_to_timespec_secs(content, &ret)) {
g_free(content);
return timespec_failure(ret);
}
@@ -567,7 +563,7 @@ dom_tree_to_timespec(xmlNodePtr node)
return timespec_failure(ret);
}
if(!string_to_timespec_nsecs(content, ret)) {
if(!string_to_timespec_nsecs(content, &ret)) {
g_free(content);
return timespec_failure(ret);
}
@@ -732,7 +728,7 @@ dom_tree_to_commodity_ref(xmlNodePtr node, QofBook *book)
daref = dom_tree_to_commodity_ref_no_engine(node, book);
table = gnc_book_get_commodity_table (book);
table = gnc_commodity_table_get_table (book);
g_return_val_if_fail (table != NULL, NULL);
@@ -750,7 +746,7 @@ dom_tree_to_commodity_ref(xmlNodePtr node, QofBook *book)
/***********************************************************************/
/* generic parser */
static void
static inline void
dom_tree_handlers_reset(struct dom_tree_handler *handlers)
{
for(;handlers->tag != NULL; handlers++)
@@ -759,7 +755,7 @@ dom_tree_handlers_reset(struct dom_tree_handler *handlers)
}
}
static gboolean
static inline gboolean
dom_tree_handlers_all_gotten_p(struct dom_tree_handler *handlers)
{
gboolean ret = TRUE;
@@ -776,7 +772,7 @@ dom_tree_handlers_all_gotten_p(struct dom_tree_handler *handlers)
}
static gboolean
static inline gboolean
gnc_xml_set_data(const gchar* tag, xmlNodePtr node, gpointer item,
struct dom_tree_handler *handlers)
{

View File

@@ -45,7 +45,8 @@ gnc_commodity *dom_tree_to_commodity_ref_no_engine(xmlNodePtr node, QofBook *);
FreqSpec* dom_tree_to_freqSpec( xmlNodePtr node, QofBook *book);
Timespec* dom_tree_to_timespec(xmlNodePtr node);
Timespec dom_tree_to_timespec(xmlNodePtr node);
#define is_valid_timespec(ts) (ts.tv_sec || ts.tv_nsec)
GDate* dom_tree_to_gdate(xmlNodePtr node);
gnc_numeric* dom_tree_to_gnc_numeric(xmlNodePtr node);
gchar * dom_tree_to_text(xmlNodePtr tree);

View File

@@ -162,7 +162,7 @@ sixtp_set_any(sixtp *tochange, int cleanup, ...)
if(!tochange)
{
PWARN("Null tochange passed\n");
PWARN("Null tochange passed");
return NULL;
}
@@ -222,7 +222,7 @@ sixtp_set_any(sixtp *tochange, int cleanup, ...)
default:
va_end(ap);
PERR("Bogus sixtp type %d\n", type);
PERR("Bogus sixtp type %d", type);
if(cleanup)
{
sixtp_destroy(tochange);
@@ -262,13 +262,13 @@ sixtp_destroy_child(gpointer key, gpointer value, gpointer user_data)
if(!corpses)
{
PERR("no corpses in sixtp_destroy_child <%s>\n",
PERR("no corpses in sixtp_destroy_child <%s>",
key ? (char *) key : "(null)");
return;
}
if(!child)
{
PERR("no child in sixtp_destroy_child <%s>\n",
PERR("no child in sixtp_destroy_child <%s>",
key ? (char *) key : "");
return;
}
@@ -339,7 +339,7 @@ sixtp_add_some_sub_parsers(sixtp *tochange, int cleanup, ...)
handler = va_arg(ap, sixtp*);
if(!handler)
{
PWARN("Handler for tag %s is null\n",
PWARN("Handler for tag %s is null",
tag ? tag : "(null)");
if(cleanup)
@@ -614,7 +614,7 @@ sixtp_handle_catastrophe(sixtp_sax_data *sax_data)
GSList *lp;
GSList **stack = &(sax_data->stack);
PERR("parse failed at \n");
PERR("parse failed at:");
sixtp_print_frame_stack(sax_data->stack, stderr);
while(*stack)
@@ -832,7 +832,7 @@ gnc_is_our_xml_file(const char *filename, const char *first_tag)
return FALSE;
}
tag_compare = g_strdup_printf("<%s>", first_tag);
tag_compare = g_strdup_printf("<%s", first_tag);
result = (strncmp(cursor, tag_compare, strlen(tag_compare)) == 0);
g_free (tag_compare);

View File

@@ -82,7 +82,7 @@ test_dom_tree_to_text(void)
}
else
{
failure_args("dom_tree_to_text", __FILE__, __LINE__,
failure_args("dom_tree_to_text", __FILE__, __LINE__,
"with string %s", test_string1);
}
@@ -100,7 +100,7 @@ test_dom_tree_to_timespec(void)
for(i = 0; i < 20; i++)
{
Timespec *test_spec1;
Timespec *test_spec2;
Timespec test_spec2;
xmlNodePtr test_node;
test_spec1 = get_random_timespec();
@@ -109,7 +109,7 @@ test_dom_tree_to_timespec(void)
test_spec2 = dom_tree_to_timespec(test_node);
if(test_spec2 == NULL)
if(!is_valid_timespec(test_spec2))
{
failure_args("dom_tree_to_timespec",
__FILE__, __LINE__, "NULL return");
@@ -118,7 +118,7 @@ test_dom_tree_to_timespec(void)
printf("\n");
}
else if(timespec_cmp(test_spec1, test_spec2) == 0)
else if(timespec_cmp(test_spec1, &test_spec2) == 0)
{
success("dom_tree_to_timespec");
}
@@ -130,14 +130,13 @@ test_dom_tree_to_timespec(void)
printf("\n");
printf("Secs are %lld vs %lld :: ",
test_spec1->tv_sec,
test_spec2->tv_sec);
test_spec2.tv_sec);
printf("NSecs are %ld vs %ld\n",
test_spec1->tv_nsec,
test_spec2->tv_nsec);
test_spec2.tv_nsec);
}
g_free(test_spec1);
g_free(test_spec2);
xmlFreeNode(test_node);
}
}
@@ -223,7 +222,7 @@ test_dom_tree_to_guid(void)
if (!(test_node = guid_to_dom_tree("test-guid", test_guid1)))
{
failure_args("guid_to_dom_tree", __FILE__, __LINE__,
failure_args("guid_to_dom_tree", __FILE__, __LINE__,
"conversion to dom tree failed");
}

View File

@@ -242,15 +242,15 @@ equals_node_val_vs_kvp_frame(xmlNodePtr node, const kvp_frame *frm)
gboolean
equals_node_val_vs_date(xmlNodePtr node, const Timespec tm)
{
Timespec *tm_test = dom_tree_to_timespec(node);
Timespec tm_test = dom_tree_to_timespec(node);
if(tm_test->tv_sec == tm.tv_sec && tm_test->tv_nsec == tm.tv_nsec)
if(tm_test.tv_sec == tm.tv_sec && tm_test.tv_nsec == tm.tv_nsec)
{
return TRUE;
}
else
{
return TRUE;
return FALSE;
}
}

View File

@@ -51,7 +51,7 @@ guile_main (void *closure, int argc, char **argv)
if (!location)
{
location = "../../../../accounts/C";
location = "../../../../accounts/C";
}
gnc_module_system_init();

View File

@@ -1,3 +1,8 @@
/* @file test-load-xml2.c
* @breif test the loading of a vrsion-2 gnucash XML file
*/
#include <glib.h>
#include <libguile.h>
#include <stdlib.h>
@@ -12,6 +17,8 @@
#include "Group.h"
#include "TransLog.h"
#include "gnc-engine.h"
#include "gnc-backend-file.h"
#include "qofsession-p.h"
#include "gnc-module.h"
#include "io-gncxml-v2.h"
@@ -47,6 +54,7 @@ test_load_file(const char *filename)
{
QofSession *session;
QofBook *book;
QofBackend *be;
gboolean ignore_lock;
session = qof_session_new();
@@ -56,9 +64,9 @@ test_load_file(const char *filename)
ignore_lock = (strcmp(getenv("SRCDIR"), ".") != 0);
qof_session_begin(session, filename, ignore_lock, FALSE);
qof_session_load_from_xml_file_v2(session);
book = qof_session_get_book (session);
be = qof_session_get_backend (session);
qof_session_load_from_xml_file_v2((FileBackend *)be, book);
do_test (xaccGroupGetBook (xaccGetAccountGroup (book)) == book,
"book and group don't match");
@@ -79,7 +87,7 @@ guile_main (void *closure, int argc, char **argv)
if (!location)
{
location = "test-files/xml2";
location = "test-files/xml2";
}
gnc_module_system_init();

View File

@@ -97,14 +97,6 @@ node_and_account_equal(xmlNodePtr node, Account *act)
return g_strdup("commodities differ");
}
}
else if(safe_strcmp(mark->name, "act:currency") == 0)
{
if(!equals_node_val_vs_commodity(
mark, DxaccAccountGetCurrency(act), xaccAccountGetBook(act)))
{
return g_strdup("currencies differ");
}
}
else if(safe_strcmp(mark->name, "act:code") == 0)
{
if(!equals_node_val_vs_string(mark, xaccAccountGetCode(act)))
@@ -120,17 +112,9 @@ node_and_account_equal(xmlNodePtr node, Account *act)
return g_strdup("descriptions differ");
}
}
else if(safe_strcmp(mark->name, "act:security") == 0)
{
if(!equals_node_val_vs_commodity(
mark, DxaccAccountGetSecurity(act), xaccAccountGetBook(act)))
{
return g_strdup("securities differ");
}
}
else if(safe_strcmp(mark->name, "act:slots") == 0)
{
xaccAccountDeleteOldData (act);
/* xaccAccountDeleteOldData (act); */
if(!equals_node_val_vs_kvp_frame(mark, xaccAccountGetSlots(act)))
{
@@ -153,20 +137,6 @@ node_and_account_equal(xmlNodePtr node, Account *act)
return g_strdup("commodity scus differ");
}
}
else if(safe_strcmp(mark->name, "act:currency-scu") == 0)
{
if(!equals_node_val_vs_int(mark, DxaccAccountGetCurrencySCU(act)))
{
return g_strdup("currency scus differ");
}
}
else if(safe_strcmp(mark->name, "act:security-scu") == 0)
{
if(!equals_node_val_vs_int(mark, xaccAccountGetCommoditySCU(act)))
{
return g_strdup("commodity scus differ");
}
}
else
{
return g_strdup_printf("unknown node in dom tree: %s", mark->name);
@@ -215,7 +185,7 @@ test_add_account(const char *tag, gpointer globaldata, gpointer data)
do_test_args(xaccAccountEqual((Account*)data, (Account*)(gdata->act),
TRUE),
"gnc_account_sixtp_parser_create",
__FILE__, __LINE__, "%d", gdata->value );
__FILE__, __LINE__, "%d", gdata->value );
return TRUE;
}

View File

@@ -130,8 +130,8 @@ test_add_commodity(const char *tag, gpointer globaldata, gpointer data)
com_data *gdata = (com_data*)globaldata;
do_test_args(gnc_commodity_equiv((gnc_commodity*)data, gdata->com),
"gnc_commodity_sixtp_parser_create",
__FILE__, __LINE__, "%d", gdata->value );
"gnc_commodity_sixtp_parser_create",
__FILE__, __LINE__, "%d", gdata->value );
gnc_commodity_destroy((gnc_commodity*)data);
return TRUE;
@@ -155,7 +155,7 @@ test_generation(void)
test_node = gnc_commodity_dom_tree_create(ran_com);
if(!test_node)
{
failure_args("commodity_xml", __FILE__, __LINE__,
failure_args("commodity_xml", __FILE__, __LINE__,
"gnc_commodity_dom_tree_create returned NULL");
gnc_commodity_destroy(ran_com);
continue;
@@ -197,7 +197,7 @@ test_generation(void)
(gpointer)&data, book))
{
failure_args("gnc_xml_parse_file returned FALSE",
__FILE__, __LINE__, "%d", i);
__FILE__, __LINE__, "%d", i);
}
/* no handling of circular data structures. We'll do that later */

View File

@@ -123,21 +123,21 @@ equals_node_val_vs_split_internal(xmlNodePtr node, Split* spl)
else if(safe_strcmp(mark->name, "split:value") == 0)
{
gnc_numeric *num = dom_tree_to_gnc_numeric(mark);
gnc_numeric val = xaccSplitGetValue(spl);
gnc_numeric val = xaccSplitGetValue(spl);
if(!gnc_numeric_equal(*num, val))
{
g_free(num);
return g_strdup_printf ("values differ: %lld/%lld v %lld/%lld",
(*num).num, (*num).denom,
val.num, val.denom);
(*num).num, (*num).denom,
val.num, val.denom);
}
g_free(num);
}
else if(safe_strcmp(mark->name, "split:quantity") == 0)
{
gnc_numeric *num = dom_tree_to_gnc_numeric(mark);
gnc_numeric val = xaccSplitGetAmount(spl);
gnc_numeric val = xaccSplitGetAmount(spl);
if (!gnc_numeric_equal(*num, val)) {
return g_strdup_printf( "quantities differ under _equal: %lld/%lld v %lld/%lld",
@@ -148,8 +148,8 @@ equals_node_val_vs_split_internal(xmlNodePtr node, Split* spl)
{
g_free(num);
return g_strdup_printf ("quantities differ: %lld/%lld v %lld/%lld",
(*num).num, (*num).denom,
val.num, val.denom);
(*num).num, (*num).denom,
val.num, val.denom);
}
g_free(num);
}
@@ -191,7 +191,7 @@ equals_node_val_vs_splits(xmlNodePtr node, const Transaction *trn)
return "no matching split found";
}
msg = equals_node_val_vs_split_internal(spl_node, spl_mark);
msg = equals_node_val_vs_split_internal(spl_node, spl_mark);
if(msg != NULL)
{
return msg;
@@ -276,10 +276,10 @@ node_and_transaction_equal(xmlNodePtr node, Transaction *trn)
}
else if(safe_strcmp(mark->name, "trn:splits") == 0)
{
char *msg = equals_node_val_vs_splits (mark, trn);
char *msg = equals_node_val_vs_splits (mark, trn);
if(msg != NULL)
{
return msg;
return msg;
}
}
else
@@ -319,9 +319,9 @@ test_add_transaction(const char *tag, gpointer globaldata, gpointer data)
xaccTransCommitEdit (trans);
if (!do_test_args(xaccTransEqual(gdata->trn, trans, TRUE, TRUE, TRUE, FALSE),
"gnc_transaction_sixtp_parser_create",
__FILE__, __LINE__,
"%d", gdata->value))
"gnc_transaction_sixtp_parser_create",
__FILE__, __LINE__,
"%d", gdata->value))
return FALSE;
gdata->new_trn = trans;
@@ -346,7 +346,7 @@ test_transaction(void)
ran_trn = get_random_transaction(book);
{
/* xaccAccountInsertSplit can reorder the splits. */
/* xaccAccountInsertSplit can reorder the splits. */
GList * list = g_list_copy(xaccTransGetSplitList (ran_trn));
GList * node = list;
for ( ; node; node = node->next)
@@ -359,7 +359,7 @@ test_transaction(void)
xaccAccountInsertSplit (a, s);
xaccAccountCommitEdit (a);
}
g_list_free(list);
g_list_free(list);
}
com = xaccTransGetCurrency (ran_trn);
@@ -428,7 +428,7 @@ test_transaction(void)
(gpointer)&data, book))
{
failure_args("gnc_xml_parse_file returned FALSE",
__FILE__, __LINE__, "%d", i);
__FILE__, __LINE__, "%d", i);
}
else
really_get_rid_of_transaction (data.new_trn);

View File

@@ -17,7 +17,7 @@ main(int argc, char **argv)
if (!directory)
{
directory = "../../../../accounts/C";
directory = "../../../../accounts/C";
}
filename = malloc(strlen(directory) + 1 + strlen(FILENAME) + 1);

View File

@@ -53,7 +53,8 @@ noinst_HEADERS = \
putil.h \
txn.h \
txnmass.h \
upgrade.h
upgrade.h \
newtables.h
EXTRA_DIST = \
.cvsignore \

View File

@@ -669,9 +669,9 @@ pgendFillOutToCheckpoint (PGBackend *be, const char *query_string)
p = be->buff; *p = 0;
p = stpcpy (p,
"SELECT DISTINCT gncTransaction.* "
"FROM gncEntry, gncTransaction WHERE "
"gncEntry.transGuid = gncTransaction.transGuid AND "
"gncEntry.accountGuid='");
"FROM gncSplit, gncTransaction WHERE "
"gncSplit.transGuid = gncTransaction.transGuid AND "
"gncSplit.accountGuid='");
p = guid_to_string_buff(xaccAccountGetGUID(ae->acct), p);
p = stpcpy (p, "' AND gncTransaction.date_posted > '");
p = gnc_timespec_to_iso8601_buff (ae->ts, p);
@@ -965,7 +965,7 @@ pgendSyncSingleFile (QofBackend *bend, QofBook *book)
"LOCK TABLE gncAccount IN EXCLUSIVE MODE;\n"
"LOCK TABLE gncCommodity IN EXCLUSIVE MODE;\n"
"LOCK TABLE gncTransaction IN EXCLUSIVE MODE;\n"
"LOCK TABLE gncEntry IN EXCLUSIVE MODE;\n";
"LOCK TABLE gncSplit IN EXCLUSIVE MODE;\n";
SEND_QUERY (be,p, );
FINISH_QUERY(be->connection);
@@ -979,18 +979,18 @@ pgendSyncSingleFile (QofBackend *bend, QofBook *book)
/* do the one-book equivalent of "DELETE FROM gncTransaction;" */
p = buff;
p = stpcpy (p, "DELETE FROM gncTransaction WHERE "
" gncTransaction.transGuid = gncEntry.transGuid AND "
" gncEntry.accountGuid = gncAccount.accountGuid AND "
" gncTransaction.transGuid = gncSplit.transGuid AND "
" gncSplit.accountGuid = gncAccount.accountGuid AND "
" gncAccount.bookGuid = '");
p = stpcpy (p, book_guid);
p = stpcpy (p, "';");
SEND_QUERY (be,buff, );
FINISH_QUERY(be->connection);
/* do the one-book equivalent of "DELETE FROM gncEntry;" */
/* do the one-book equivalent of "DELETE FROM gncSplit;" */
p = buff;
p = stpcpy (p, "DELETE FROM gncEntry WHERE "
" gncEntry.accountGuid = gncAccount.accountGuid AND "
p = stpcpy (p, "DELETE FROM gncSplit WHERE "
" gncSplit.accountGuid = gncAccount.accountGuid AND "
" gncAccount.bookGuid = '");
p = stpcpy (p, book_guid);
p = stpcpy (p, "';");
@@ -1975,7 +1975,7 @@ pgend_session_begin (QofBackend *backend,
* Alas, it does not, so we just bomb out.
*/
qof_backend_set_error (&be->be, ERR_BACKEND_CANT_CONNECT);
qof_backend_set_message(&be->be, msg);
qof_backend_set_message(&be->be, _("From the Postgresql Server: %s"), msg);
return;
}
@@ -2011,7 +2011,7 @@ pgend_session_begin (QofBackend *backend,
/* Check to see if we have a database version that we can
* live with */
rc = pgendDBVersionIsCurrent (be);
if (0 > rc)
if (rc < 0)
{
/* The server is newer than we are, or another error occured,
* we don't know how to talk to it. The err code is already set. */
@@ -2019,7 +2019,7 @@ pgend_session_begin (QofBackend *backend,
be->connection = NULL;
return;
}
if (0 < rc)
if (rc > 0)
{
/* The server is older than we are; ask user if they want to
* upgrade the database contents. */
@@ -2191,7 +2191,7 @@ pgend_session_begin (QofBackend *backend,
/* But first, make sure all users are logged off ... */
p = "BEGIN;\n"
"LOCK TABLE gncSession IN ACCESS EXCLUSIVE MODE;\n"
"SELECT time_off FROM gncSession WHERE time_off ='infinity';";
"SELECT time_off FROM gncSession WHERE time_off ='infinity';\n";
SEND_QUERY (be,p, );
someones_still_on =
GPOINTER_TO_INT (pgendGetResults (be, has_results_cb,
@@ -2204,10 +2204,10 @@ pgend_session_begin (QofBackend *backend,
qof_backend_set_error (&be->be, ERR_SQL_DB_BUSY);
return;
}
pgendUpgradeDB (be);
p = "COMMIT;\n";
SEND_QUERY (be,p, );
FINISH_QUERY(be->connection);
pgendUpgradeDB (be);
}
else
{

View File

@@ -98,7 +98,7 @@ pgendAccountRecomputeAllCheckpoints (PGBackend *be, const GUID *acct_guid)
* lock) */
p = "BEGIN WORK;\n"
"LOCK TABLE gncCheckpoint IN ACCESS EXCLUSIVE MODE;\n"
"LOCK TABLE gncEntry IN SHARE MODE;\n";
"LOCK TABLE gncSplit IN SHARE MODE;\n";
SEND_QUERY (be,p, );
FINISH_QUERY(be->connection);
@@ -126,10 +126,10 @@ pgendAccountRecomputeAllCheckpoints (PGBackend *be, const GUID *acct_guid)
{
p = be->buff; *p = 0;
p = stpcpy (p, "SELECT gncTransaction.date_posted"
" FROM gncTransaction, gncEntry"
" FROM gncTransaction, gncSplit"
" WHERE"
" gncEntry.transguid = gncTransaction.transguid AND"
" gncEntry.accountguid='");
" gncSplit.transguid = gncTransaction.transguid AND"
" gncSplit.accountguid='");
p = stpcpy (p, guid_string);
p = stpcpy (p, "'"
" ORDER BY gncTransaction.date_posted ASC"
@@ -251,7 +251,7 @@ pgendAccountRecomputeOneCheckpoint (PGBackend *be, Account *acc, Timespec ts)
p = be->buff; *p = 0;
p = stpcpy (p, "BEGIN WORK;\n"
"LOCK TABLE gncCheckpoint IN ACCESS EXCLUSIVE MODE;\n"
"LOCK TABLE gncEntry IN SHARE MODE;\n"
"LOCK TABLE gncSplit IN SHARE MODE;\n"
"UPDATE gncCheckpoint SET "
" balance = (gncsubtotalbalance (accountGuid, date_start, date_end )),"
" cleared_balance = (gncsubtotalclearedbalance (accountGuid, date_start, date_end )),"
@@ -282,15 +282,15 @@ pgendTransactionRecomputeCheckpoints (PGBackend *be, Transaction *trans)
p = stpcpy (p, "BEGIN WORK;\n"
"LOCK TABLE gncCheckpoint IN ACCESS EXCLUSIVE MODE;\n"
"LOCK TABLE gncTransaction IN SHARE MODE;\n"
"LOCK TABLE gncEntry IN SHARE MODE;\n"
"LOCK TABLE gncSplit IN SHARE MODE;\n"
"UPDATE gncCheckpoint SET "
" balance = (gncsubtotalbalance (gncEntry.accountGuid, date_start, date_end )),"
" cleared_balance = (gncsubtotalclearedbalance (gncEntry.accountGuid, date_start, date_end )),"
" reconciled_balance = (gncsubtotalreconedbalance (gncEntry.accountGuid, date_start, date_end )) "
" WHERE gncEntry.transGuid = '");
" balance = (gncsubtotalbalance (gncSplit.accountGuid, date_start, date_end )),"
" cleared_balance = (gncsubtotalclearedbalance (gncSplit.accountGuid, date_start, date_end )),"
" reconciled_balance = (gncsubtotalreconedbalance (gncSplit.accountGuid, date_start, date_end )) "
" WHERE gncSplit.transGuid = '");
p = guid_to_string_buff (xaccTransGetGUID(trans), p);
p = stpcpy (p, "' AND gncTransaction.transGuid = gncEntry.transGuid "
" AND gncCheckpoint.accountGuid = gncEntry.accountGuid "
p = stpcpy (p, "' AND gncTransaction.transGuid = gncSplit.transGuid "
" AND gncCheckpoint.accountGuid = gncSplit.accountGuid "
" AND date_start <= gncTransaction.date_posted "
" AND date_end > gncTransaction.date_posted;\n"
"COMMIT WORK;\n"

View File

@@ -261,7 +261,7 @@ pgendProcessEvents (QofBackend *bend)
GET_EVENTS (transGuid, gncTransactionTrail, be->last_transaction);
/* gnc_cm_event_handler() doesn't really want to see any split guids */
// GET_EVENTS (entryGuid, gncEntryTrail, be->last_transaction);
// GET_EVENTS (splitGuid, gncSplitTrail, be->last_transaction);
}
/* Loop over each item, updating the engine, and dispatching events */

View File

@@ -12,58 +12,58 @@
" \n"
"-- utility functions to compute checkpoint balance subtotals \n"
" \n"
"CREATE FUNCTION gncSubtotalBalance (CHAR(32), TIMESTAMP, TIMESTAMP) \n"
"CREATE FUNCTION gncSubtotalBalance (CHAR(32), TIMESTAMP WITH TIME ZONE, TIMESTAMP WITH TIME ZONE) \n"
" RETURNS INT8 \n"
" AS 'SELECT INT8(sum(gncEntry.amount)) \n"
" FROM gncEntry, gncTransaction \n"
" AS 'SELECT INT8(sum(gncSplit.amount)) \n"
" FROM gncSplit, gncTransaction \n"
" WHERE \n"
" gncEntry.accountGuid = $1 AND \n"
" gncEntry.transGuid = gncTransaction.transGuid AND \n"
" gncSplit.accountGuid = $1 AND \n"
" gncSplit.transGuid = gncTransaction.transGuid AND \n"
" gncTransaction.date_posted BETWEEN $2 AND $3' \n"
" LANGUAGE 'sql'; \n"
" \n"
"CREATE FUNCTION gncSubtotalClearedBalance (CHAR(32), TIMESTAMP, TIMESTAMP) \n"
"CREATE FUNCTION gncSubtotalClearedBalance (CHAR(32), TIMESTAMP WITH TIME ZONE, TIMESTAMP WITH TIME ZONE) \n"
" RETURNS INT8 \n"
" AS 'SELECT INT8(sum(gncEntry.amount)) \n"
" FROM gncEntry, gncTransaction \n"
" AS 'SELECT INT8(sum(gncSplit.amount)) \n"
" FROM gncSplit, gncTransaction \n"
" WHERE \n"
" gncEntry.accountGuid = $1 AND \n"
" gncEntry.transGuid = gncTransaction.transGuid AND \n"
" gncSplit.accountGuid = $1 AND \n"
" gncSplit.transGuid = gncTransaction.transGuid AND \n"
" gncTransaction.date_posted BETWEEN $2 AND $3 AND \n"
" gncEntry.reconciled <> \\'n\\'' \n"
" gncSplit.reconciled <> \\'n\\'' \n"
" LANGUAGE 'sql'; \n"
" \n"
"CREATE FUNCTION gncSubtotalReconedBalance (CHAR(32), TIMESTAMP, TIMESTAMP) \n"
"CREATE FUNCTION gncSubtotalReconedBalance (CHAR(32), TIMESTAMP WITH TIME ZONE, TIMESTAMP WITH TIME ZONE) \n"
" RETURNS INT8 \n"
" AS 'SELECT INT8(sum(gncEntry.amount)) \n"
" FROM gncEntry, gncTransaction \n"
" AS 'SELECT INT8(sum(gncSplit.amount)) \n"
" FROM gncSplit, gncTransaction \n"
" WHERE \n"
" gncEntry.accountGuid = $1 AND \n"
" gncEntry.transGuid = gncTransaction.transGuid AND \n"
" gncSplit.accountGuid = $1 AND \n"
" gncSplit.transGuid = gncTransaction.transGuid AND \n"
" gncTransaction.date_posted BETWEEN $2 AND $3 AND \n"
" (gncEntry.reconciled = \\'y\\' OR \n"
" gncEntry.reconciled = \\'f\\')' \n"
" (gncSplit.reconciled = \\'y\\' OR \n"
" gncSplit.reconciled = \\'f\\')' \n"
" LANGUAGE 'sql'; \n"
" \n"
"-- helper functions. These intentionally use the 'wrong' fraction. \n"
"-- This is because value_frac * amount * price = value * amount_frac \n"
" \n"
"CREATE FUNCTION gncHelperPrVal (gncEntry) \n"
"CREATE FUNCTION gncHelperPrVal (gncSplit) \n"
" RETURNS INT8 \n"
" AS 'SELECT abs($1 . value * gncCommodity.fraction) \n"
" FROM gncEntry, gncAccount, gncCommodity \n"
" FROM gncSplit, gncAccount, gncCommodity \n"
" WHERE \n"
" $1 . accountGuid = gncAccount.accountGuid AND \n"
" gncAccount.commodity = gncCommodity.commodity' \n"
" LANGUAGE 'sql'; \n"
" \n"
"CREATE FUNCTION gncHelperPrAmt (gncEntry) \n"
"CREATE FUNCTION gncHelperPrAmt (gncSplit) \n"
" RETURNS INT8 \n"
" AS 'SELECT abs($1 . amount * gncCommodity.fraction) \n"
" FROM gncEntry, gncTransaction, gncCommodity \n"
" FROM gncSplit, gncTransaction, gncCommodity \n"
" WHERE \n"
" $1 . transGuid = gncTransaction.transGuid AND \n"
" gncTransaction.currency = gncCommodity.commodity' \n"
" LANGUAGE 'sql'; \n"
" \n"
"-- end of file";
"-- end of file"

View File

@@ -135,13 +135,13 @@ sql_sort_get_type(char *p, GSList * path)
}
} else if (!safe_strcmp(path->data, SPLIT_DATE_RECONCILED)) {
p = stpcpy(p, "gncEntry.date_reconciled");
p = stpcpy(p, "gncSplit.date_reconciled");
} else if (!safe_strcmp(path->data, SPLIT_MEMO)) {
p = stpcpy(p, "gncEntry.memo");
p = stpcpy(p, "gncSplit.memo");
} else if (!safe_strcmp(path->data, SPLIT_RECONCILE)) {
p = stpcpy(p, "gncEntry.reconciled");
p = stpcpy(p, "gncSplit.reconciled");
} else if (!safe_strcmp(path->data, SPLIT_VALUE)) {
p = stpcpy(p, "gncEntry.amount");
p = stpcpy(p, "gncSplit.amount");
} else if (!safe_strcmp(path->data, SPLIT_ACCT_FULLNAME)) {
/* XXX hack alert FIXME implement this */
PERR("BY_ACCOUNT_FULL_NAME badly implemented");
@@ -326,20 +326,20 @@ sql_sort_need_entry(QofQuery * q)
\
if (invert) \
sq->pq = stpcpy (sq->pq, "NOT ("); \
if (pd->how == QOF_COMPARE_NEQ) \
if (pd->how == QOF_COMPARE_NEQ) \
sq->pq = stpcpy (sq->pq, "NOT ("); \
if (pdata->is_regex || pdata->options == QOF_STRING_MATCH_CASEINSENSITIVE) \
sq->pq = stpcpy(sq->pq, fieldname " ~"); \
else \
sq->pq = stpcpy(sq->pq, fieldname " ="); \
if (pdata->options == QOF_STRING_MATCH_CASEINSENSITIVE) { \
if (pdata->options == QOF_STRING_MATCH_CASEINSENSITIVE) { \
sq->pq = stpcpy(sq->pq, "*"); \
} \
sq->pq = stpcpy(sq->pq, " '"); \
tmp = sqlEscapeString (sq->escape, pdata->matchstring); \
sq->pq = stpcpy(sq->pq, tmp); \
sq->pq = stpcpy(sq->pq, "'"); \
if (pd->how == QOF_COMPARE_NEQ) \
if (pd->how == QOF_COMPARE_NEQ) \
sq->pq = stpcpy (sq->pq, ") "); \
if (invert) \
sq->pq = stpcpy (sq->pq, ") "); \
@@ -355,16 +355,16 @@ sql_sort_need_entry(QofQuery * q)
#define AMOUNT_TERM(signcheck,fieldname,comtable) \
{ \
double amt = gnc_numeric_to_double (pdata->amount); \
\
\
if (invert) \
sq->pq = stpcpy (sq->pq, "NOT ("); \
\
switch(pdata->options) \
{ \
case QOF_NUMERIC_MATCH_CREDIT: \
case QOF_NUMERIC_MATCH_CREDIT: \
sq->pq = stpcpy(sq->pq, signcheck " <= 0 AND "); \
break; \
case QOF_NUMERIC_MATCH_DEBIT: \
case QOF_NUMERIC_MATCH_DEBIT: \
sq->pq = stpcpy(sq->pq, signcheck " >= 0 AND "); \
break; \
default: \
@@ -372,36 +372,36 @@ sql_sort_need_entry(QofQuery * q)
} \
switch(pd->how) \
{ \
case QOF_COMPARE_GTE: \
case QOF_COMPARE_GTE: \
sq->pq = stpcpy(sq->pq, \
fieldname " >= "comtable" * float8"); \
sq->pq += sprintf (sq->pq, "(%22.18g)", amt); \
sq->pq += sprintf (sq->pq, "(%22.18g)", amt); \
break; \
case QOF_COMPARE_GT: \
case QOF_COMPARE_GT: \
sq->pq = stpcpy(sq->pq, \
fieldname " > "comtable" * float8"); \
sq->pq += sprintf (sq->pq, "(%22.18g)", amt); \
fieldname " > "comtable" * float8"); \
sq->pq += sprintf (sq->pq, "(%22.18g)", amt); \
break; \
case QOF_COMPARE_LTE: \
case QOF_COMPARE_LTE: \
sq->pq = stpcpy(sq->pq, \
fieldname " <= "comtable" * float8"); \
sq->pq += sprintf (sq->pq, "(%22.18g)", amt); \
fieldname " <= "comtable" * float8"); \
sq->pq += sprintf (sq->pq, "(%22.18g)", amt); \
break; \
case QOF_COMPARE_LT: \
case QOF_COMPARE_LT: \
sq->pq = stpcpy(sq->pq, \
fieldname " < "comtable" * float8"); \
sq->pq += sprintf (sq->pq, "(%22.18g)", amt); \
fieldname " < "comtable" * float8"); \
sq->pq += sprintf (sq->pq, "(%22.18g)", amt); \
break; \
case QOF_COMPARE_EQUAL: \
case QOF_COMPARE_EQUAL: \
sq->pq = stpcpy(sq->pq, \
"abs(" fieldname " - abs("comtable" * float8"); \
sq->pq += sprintf (sq->pq, "(%22.18g)", amt); \
"abs(" fieldname " - abs("comtable" * float8"); \
sq->pq += sprintf (sq->pq, "(%22.18g)", amt); \
sq->pq = stpcpy(sq->pq, ")) < 1"); \
break; \
case QOF_COMPARE_NEQ: \
case QOF_COMPARE_NEQ: \
sq->pq = stpcpy(sq->pq, \
"abs(" fieldname " - abs("comtable" * float8"); \
sq->pq += sprintf (sq->pq, "(%22.18g)", amt); \
"abs(" fieldname " - abs("comtable" * float8"); \
sq->pq += sprintf (sq->pq, "(%22.18g)", amt); \
sq->pq = stpcpy(sq->pq, ")) > 1"); \
break; \
} \
@@ -418,7 +418,7 @@ sql_sort_need_entry(QofQuery * q)
if (got_one) \
sq->pq = stpcpy(sq->pq, "OR "); \
\
sq->pq = stpcpy(sq->pq, "gncEntry.reconciled = '"); \
sq->pq = stpcpy(sq->pq, "gncSplit.reconciled = '"); \
*(sq->pq) = flagchar; (sq->pq) ++; \
sq->pq = stpcpy(sq->pq, "' "); \
got_one++; \
@@ -644,7 +644,7 @@ sqlQuery_kvp_build(sqlQuery * sq, GSList * sort_path, QofQueryCompare how,
list = NULL;
if (!safe_strcmp(sort_path->data, SPLIT_KVP))
list = g_list_prepend(list, "gncEntry");
list = g_list_prepend(list, "gncSplit");
else if (!safe_strcmp(sort_path->data, SPLIT_TRANS))
list = g_list_prepend(list, "gncTransaction");
else
@@ -814,12 +814,12 @@ sqlQuery_build(sqlQuery * sq, Query * q)
if (need_entry || need_account) {
sq->pq = stpcpy(sq->pq,
" gncEntry.transGuid = gncTransaction.transGuid AND ");
" gncSplit.transGuid = gncTransaction.transGuid AND ");
}
if (need_account) {
sq->pq = stpcpy(sq->pq,
" gncEntry.accountGuid = gncAccount.accountGuid AND ");
" gncSplit.accountGuid = gncAccount.accountGuid AND ");
}
sq->pq = stpcpy(sq->pq, " ( ");
@@ -863,24 +863,24 @@ sqlQuery_build(sqlQuery * sq, Query * q)
PINFO("term is QOF_QUERYCORE_GUID");
if (!safe_strcmp(path->data, QOF_QUERY_PARAM_GUID)) {
field = "gncEntry.entryGUID";
field = "gncSplit.splitGuid";
g_assert(pdata->options != QOF_GUID_MATCH_ALL);
} else if (!safe_strcmp(path->data, SPLIT_TRANS) &&
!safe_strcmp(path->next->data, QOF_QUERY_PARAM_GUID)) {
field = "gncEntry.transGUID";
field = "gncSplit.transGUID";
g_assert(pdata->options != QOF_GUID_MATCH_ALL);
} else if (!safe_strcmp(path->data, SPLIT_ACCOUNT) &&
!safe_strcmp(path->next->data, QOF_QUERY_PARAM_GUID)) {
field = "gncEntry.accountGUID";
field = "gncSplit.accountGUID";
g_assert(pdata->options != QOF_GUID_MATCH_ALL);
} else if (!safe_strcmp(path->data, SPLIT_TRANS) &&
!safe_strcmp(path->next->data, TRANS_SPLITLIST) &&
!safe_strcmp(path->next->next->data,
SPLIT_ACCOUNT_GUID)) {
field = "gncEntry.accountGUID";
field = "gncSplit.accountGUID";
g_assert(pdata->options == QOF_GUID_MATCH_ALL);
} else if (!safe_strcmp(path->data, QOF_QUERY_PARAM_BOOK) &&
@@ -916,7 +916,7 @@ sqlQuery_build(sqlQuery * sq, Query * q)
case QOF_GUID_MATCH_ALL:
sq->pq = stpcpy(sq->pq,
" EXISTS ( SELECT true FROM gncEntry e"
" EXISTS ( SELECT true FROM gncSplit e"
" WHERE "
"e.transGuid = gncTransaction.transGuid"
" AND " "e.accountGuid='");
@@ -964,11 +964,11 @@ sqlQuery_build(sqlQuery * sq, Query * q)
if (!safe_strcmp(path->data, SPLIT_ACTION)) {
PINFO("term is PR_ACTION");
STRING_TERM("gncEntry.action");
STRING_TERM("gncSplit.action");
} else if (!safe_strcmp(path->data, SPLIT_MEMO)) {
PINFO("term is PR_MEMO");
STRING_TERM("gncEntry.memo");
STRING_TERM("gncSplit.memo");
} else if (!safe_strcmp(path->data, SPLIT_TRANS)) {
path = path->next;
@@ -999,7 +999,7 @@ sqlQuery_build(sqlQuery * sq, Query * q)
PINFO("term is PR_SHRS");
sq->pq = stpcpy(sq->pq,
"gncAccount.commodity = account_com.commodity AND ");
AMOUNT_TERM("gncEntry.amount", "abs(gncEntry.amount)",
AMOUNT_TERM("gncSplit.amount", "abs(gncSplit.amount)",
"account_com.fraction");
} else if (!safe_strcmp(path->data, SPLIT_VALUE)) {
@@ -1007,16 +1007,16 @@ sqlQuery_build(sqlQuery * sq, Query * q)
PINFO("term is PR_VALUE");
sq->pq = stpcpy(sq->pq,
"gncTransaction.currency = trans_com.commodity AND ");
AMOUNT_TERM("gncEntry.value", "abs(gncEntry.value)",
AMOUNT_TERM("gncSplit.value", "abs(gncSplit.value)",
"trans_com.fraction");
} else if (!safe_strcmp(path->data, SPLIT_SHARE_PRICE)) {
PINFO("term is PR_PRICE");
AMOUNT_TERM("gncEntry.value / gncEntry.amount",
"gncHelperPrVal(gncEntry)",
"gncHelperPrAmt(gncEntry)");
AMOUNT_TERM("gncSplit.value / gncSplit.amount",
"gncHelperPrVal(gncSplit)",
"gncHelperPrAmt(gncSplit)");
} else {
PINFO("Unknown NUMERIC: %s", (char *)(path->data));

View File

@@ -32,8 +32,8 @@
* AND'ed together; the outer list OR's together the inner lists.
*
* The resulting query will resemble
* SELECT * FROM gncEntry WHERE
* (entryguid='deadbeef') OR
* SELECT * FROM gncSplit WHERE
* (splitguid='deadbeef') OR
* (memo='deposit' AND date_reconciled>'1998-07-01 11:00:00.345678 -0500')
*
* HISTORY:

View File

@@ -34,8 +34,10 @@
#include <glib.h>
#include <libpq-fe.h>
#include <stdlib.h>
#include <string.h>
#include "qofbackend.h"
#include "qofbackend-p.h"
#include "messages.h"
#include "gnc-engine-util.h"
#include "PostgresBackend.h"
@@ -43,6 +45,46 @@ static short module = MOD_BACKEND;
#include "putil.h"
ExecStatusType execQuery(PGBackend *be, const char * q) {
PGresult * result;
ExecStatusType status;
gchar * msg;
ENTER(" ");
if (!be || !be->connection) {
LEAVE("Backend or connection is not available");
qof_backend_set_message(&be->be, _("Backend connection is not available"));
qof_backend_set_error(&be->be, ERR_BACKEND_CONN_LOST);
return -1;
}
result = PQexec(be->connection, q);
if (!result) {
PINFO("Query could not be executed");
qof_backend_set_message(&be->be, _("Query could not be executed"));
qof_backend_set_error(&be->be, ERR_BACKEND_SERVER_ERR);
return -1;
}
status = PQresultStatus(result);
msg = (gchar *)PQresultErrorMessage(result);
PINFO("Result status: %s/%s",
PQresStatus(status), (strlen(msg)) > 0 ? msg : "(No Message)");
PINFO("Number of rows affected: %d", atoi(PQcmdTuples(result)));
if (status != PGRES_COMMAND_OK) {
PINFO("Query causing error: %s", q);
qof_backend_set_message(&be->be, _("From the Postgresql Server: %s"), msg);
qof_backend_set_error(&be->be, ERR_BACKEND_SERVER_ERR);
}
PQclear(result);
return status;
}
/* ============================================================= */
/* The sendQuery function sends the sql statement off to the server.
* It performs a minimal check to see that the send succeeded. The
@@ -60,7 +102,7 @@ int sendQuery(PGBackend *be,char * buff) {
gchar * msg = (gchar *)PQerrorMessage(be->connection);
PERR("send query failed:\n"
"\t%s", msg);
qof_backend_set_message(&be->be, msg);
qof_backend_set_message(&be->be, _("From the Postgresql Server: %s"), msg);
qof_backend_set_error (&be->be, ERR_BACKEND_SERVER_ERR);
return ERR_BACKEND_SERVER_ERR;
}
@@ -102,7 +144,7 @@ int finishQuery(PGBackend *be) {
gchar * msg = (gchar *)PQerrorMessage(be->connection);
PERR("finish query failed:\n\t%s", msg);
PQclear(result);
qof_backend_set_message(&be->be, msg);
qof_backend_set_message(&be->be, _("From the Postgresql Server: %s"), msg);
qof_backend_set_error (&be->be, ERR_BACKEND_SERVER_ERR);
break;
}

View File

@@ -68,6 +68,7 @@ gpointer pgendGetResults (PGBackend *be,
*/
gnc_commodity * gnc_string_to_commodity (const char *str, QofBook *book);
ExecStatusType execQuery(PGBackend *be, const char * q);
int sendQuery(PGBackend *be,char * buff);
int finishQuery(PGBackend *be);

View File

@@ -12,7 +12,7 @@
"-- \n"
"-- The change field may be 'a' -- add, 'd' -- delete/drop, 'm' -- modify \n"
"-- \n"
"-- The objtype field may be 'a' -- account, 'c' -- commodity, 'e' -- entry, \n"
"-- The objtype field may be 'a' -- account, 'c' -- commodity, 's' -- split, \n"
"-- 'k' -- kvp, 'p' -- price, 't' -- transaction \n"
"-- \n"
"-- The objtype is the class type of the child class. \n"
@@ -29,9 +29,9 @@
" \n"
"CREATE TABLE gncAuditTrail ( \n"
" sessionGuid CHAR(32) NOT NULL, -- who changed it \n"
" date_changed TIMESTAMP, -- when they changed it \n"
" change CHAR NOT NULL, \n"
" objtype CHAR NOT NULL \n"
" date_changed TIMESTAMP WITH TIME ZONE, -- when they changed it \n"
" change CHAR NOT NULL, \n"
" objtype CHAR NOT NULL \n"
"); \n"
" \n"
"-- would love to inherit, but can't because this wrecks the primary key \n"
@@ -49,8 +49,6 @@
" iguid INT4 DEFAULT 0 \n"
") INHERITS (gncAuditTrail); \n"
" \n"
"CREATE INDEX gncAccountTrail_account_idx ON gncAccountTrail (accountGuid); \n"
" \n"
"CREATE TABLE gncBookTrail ( \n"
" bookGuid CHAR(32) NOT NULL, \n"
" book_open CHAR DEFAULT 'n', \n"
@@ -58,8 +56,6 @@
" iguid INT4 DEFAULT 0 \n"
") INHERITS (gncAuditTrail); \n"
" \n"
"CREATE INDEX gncBookTrail_book_idx ON gncBookTrail (bookGuid); \n"
" \n"
"CREATE TABLE gncCommodityTrail ( \n"
" commodity TEXT NOT NULL, -- override, not a primary key anymore \n"
" fullname TEXT, \n"
@@ -69,43 +65,37 @@
" fraction INT DEFAULT '100' \n"
") INHERITS (gncAuditTrail); \n"
" \n"
"CREATE INDEX gncCommodityTrail_commodity_idx ON gncCommodityTrail (commodity); \n"
" \n"
"CREATE TABLE gncEntryTrail ( \n"
" entryGuid CHAR(32) NOT NULL, -- override, not a primary key anymore \n"
"CREATE TABLE gncSplitTrail ( \n"
" splitGuid CHAR(32) NOT NULL, -- override, not a primary key anymore \n"
" accountGuid CHAR(32) NOT NULL, \n"
" transGuid CHAR(32) NOT NULL, \n"
" memo TEXT, \n"
" action TEXT, \n"
" reconciled CHAR DEFAULT 'n', \n"
" date_reconciled TIMESTAMP, \n"
" date_reconciled TIMESTAMP WITH TIME ZONE, \n"
" amount INT8 DEFAULT '0', \n"
" value INT8 DEFAULT '0', \n"
" iguid INT4 DEFAULT 0 \n"
") INHERITS (gncAuditTrail); \n"
" \n"
"CREATE INDEX gncEntryTrail_entry_idx ON gncEntryTrail (entryGuid); \n"
" \n"
"CREATE TABLE gncPriceTrail ( \n"
" priceGuid CHAR(32) NOT NULL, -- override, not a primary key anymore \n"
" bookGuid CHAR(32) NOT NULL, \n"
" commodity TEXT NOT NULL CHECK (commodity <>''), \n"
" currency TEXT NOT NULL CHECK (commodity <>''), \n"
" time TIMESTAMP, \n"
" time TIMESTAMP WITH TIME ZONE, \n"
" source TEXT, \n"
" type TEXT, \n"
" valueNum INT8 DEFAULT '0', \n"
" valueDenom INT4 DEFAULT '100', \n"
" version INT4 NOT NULL, \n"
" bookGuid CHAR(32) NOT NULL \n"
" version INT4 NOT NULL \n"
") INHERITS (gncAuditTrail); \n"
" \n"
"CREATE INDEX gncPriceTrail_price_idx ON gncPriceTrail (priceGuid); \n"
" \n"
"CREATE TABLE gncTransactionTrail ( \n"
" transGuid CHAR(32) NOT NULL, -- override, not a primary key anymore \n"
" last_modified TIMESTAMP DEFAULT 'NOW', \n"
" date_entered TIMESTAMP, \n"
" date_posted TIMESTAMP, \n"
" last_modified TIMESTAMP WITH TIME ZONE DEFAULT 'NOW', \n"
" date_entered TIMESTAMP WITH TIME ZONE, \n"
" date_posted TIMESTAMP WITH TIME ZONE, \n"
" num TEXT, \n"
" description TEXT, \n"
" currency TEXT NOT NULL CHECK (currency <> ''), \n"
@@ -113,8 +103,6 @@
" iguid INT4 DEFAULT 0 \n"
") INHERITS (gncAuditTrail); \n"
" \n"
"CREATE INDEX gncTransactionTrail_trans_idx ON gncTransactionTrail (transGuid); \n"
" \n"
"CREATE TABLE gncKVPvalueTrail ( \n"
" iguid INT4, \n"
" ipath INT4, \n"
@@ -161,7 +149,7 @@
" iguid INT4, \n"
" ipath INT4, \n"
" type char(4), \n"
" data TIMESTAMP \n"
" data TIMESTAMP WITH TIME ZONE \n"
") INHERITS (gncAuditTrail); \n"
" \n"
"CREATE TABLE gncKVPvalue_listTrail ( \n"
@@ -171,4 +159,10 @@
" data TEXT[] \n"
") INHERITS (gncAuditTrail); \n"
" \n"
"-- end of file";
"CREATE INDEX gncAccountTrail_account_idx ON gncAccountTrail (accountGuid); \n"
"CREATE INDEX gncBookTrail_book_idx ON gncBookTrail (bookGuid); \n"
"CREATE INDEX gncCommodityTrail_commodity_idx ON gncCommodityTrail (commodity); \n"
"CREATE INDEX gncSplitTrail_split_idx ON gncSplitTrail (splitGuid); \n"
"CREATE INDEX gncPriceTrail_price_idx ON gncPriceTrail (priceGuid); \n"
"CREATE INDEX gncTransactionTrail_trans_idx ON gncTransactionTrail (transGuid); \n"
"-- end of file"

View File

@@ -27,7 +27,7 @@
" minor INT NOT NULL, \n"
" rev INT DEFAULT '0', \n"
" name TEXT UNIQUE NOT NULL CHECK (name <> ''), \n"
" date TIMESTAMP DEFAULT 'NOW' \n"
" date TIMESTAMP WITH TIME ZONE DEFAULT 'NOW' \n"
"); \n"
" \n"
"-- Commodity structure \n"
@@ -68,13 +68,11 @@
" iguid INT4 DEFAULT 0 \n"
"); \n"
" \n"
"-- CREATE INDEX gncAccount_pg_idx ON gncAccount (parentGuid); \n"
" \n"
"CREATE TABLE gncTransaction ( \n"
" transGuid CHAR(32) PRIMARY KEY, \n"
" last_modified TIMESTAMP DEFAULT 'NOW', \n"
" date_entered TIMESTAMP, \n"
" date_posted TIMESTAMP, \n"
" last_modified TIMESTAMP WITH TIME ZONE DEFAULT 'NOW', \n"
" date_entered TIMESTAMP WITH TIME ZONE, \n"
" date_posted TIMESTAMP WITH TIME ZONE, \n"
" num TEXT, \n"
" description TEXT, \n"
" currency TEXT NOT NULL CHECK (currency <> ''), \n"
@@ -82,27 +80,22 @@
" iguid INT4 DEFAULT 0 \n"
"); \n"
" \n"
"CREATE INDEX gncTransaction_posted_idx ON gncTransaction (date_posted); \n"
"-- a gncSplit is what we call 'Split' elsewhere in the engine \n"
"-- Here, we call it a 'journal split' \n"
" \n"
"-- a gncEntry is what we call 'Split' elsewhere in the engine \n"
"-- Here, we call it a 'journal entry' \n"
" \n"
"CREATE TABLE gncEntry ( \n"
" entryGuid CHAR(32) PRIMARY KEY, \n"
"CREATE TABLE gncSplit ( \n"
" splitGuid CHAR(32) PRIMARY KEY, \n"
" accountGuid CHAR(32) NOT NULL, \n"
" transGuid CHAR(32) NOT NULL, \n"
" memo TEXT, \n"
" action TEXT, \n"
" reconciled CHAR DEFAULT 'n', \n"
" date_reconciled TIMESTAMP, \n"
" date_reconciled TIMESTAMP WITH TIME ZONE, \n"
" amount INT8 DEFAULT '0', \n"
" value INT8 DEFAULT '0', \n"
" iguid INT4 DEFAULT 0 \n"
"); \n"
" \n"
"CREATE INDEX gncEntry_acc_idx ON gncEntry (accountGuid); \n"
"CREATE INDEX gncEntry_trn_idx ON gncEntry (transGuid); \n"
" \n"
"-- The checkpoint table provides balance information \n"
"-- The balance is provided in the indicated currency; \n"
"-- this allows the potential of maintaining balance information \n"
@@ -114,8 +107,8 @@
" \n"
"CREATE TABLE gncCheckpoint ( \n"
" accountGuid CHAR(32) NOT NULL, \n"
" date_start TIMESTAMP NOT NULL, \n"
" date_end TIMESTAMP NOT NULL, \n"
" date_start TIMESTAMP WITH TIME ZONE NOT NULL, \n"
" date_end TIMESTAMP WITH TIME ZONE NOT NULL, \n"
" commodity TEXT NOT NULL CHECK (commodity <>''), \n"
" type TEXT DEFAULT 'simple', \n"
" balance INT8 DEFAULT '0', \n"
@@ -129,15 +122,15 @@
"-- in units of 'currency' \n"
"CREATE TABLE gncPrice ( \n"
" priceGuid CHAR(32) PRIMARY KEY, \n"
" bookGuid CHAR(32) NOT NULL, \n"
" commodity TEXT NOT NULL CHECK (commodity <>''), \n"
" currency TEXT NOT NULL CHECK (commodity <>''), \n"
" time TIMESTAMP, \n"
" time TIMESTAMP WITH TIME ZONE, \n"
" source TEXT, \n"
" type TEXT, \n"
" valueNum INT8 DEFAULT '0', \n"
" valueDenom INT4 DEFAULT '100', \n"
" version INT4 NOT NULL, \n"
" bookGuid CHAR(32) NOT NULL \n"
" version INT4 NOT NULL \n"
"); \n"
" \n"
" \n"
@@ -157,8 +150,8 @@
" hostname TEXT, \n"
" login_name TEXT, \n"
" gecos TEXT, \n"
" time_on TIMESTAMP NOT NULL, \n"
" time_off TIMESTAMP NOT NULL DEFAULT 'INFINITY' \n"
" time_on TIMESTAMP WITH TIME ZONE NOT NULL, \n"
" time_off TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT 'INFINITY' \n"
"); \n"
" \n"
" \n"
@@ -181,8 +174,6 @@
" PRIMARY KEY (iguid, ipath) \n"
"); \n"
" \n"
"-- CREATE INDEX gncKVPvalue_iguid_idx ON gncKVPvalue (iguid); \n"
" \n"
"-- Add primary keys to each kvp table ... because key inheritance \n"
"-- is ambiguously defined and thus not implemented in postgres. \n"
"-- Note, however, adding these keys degrades performance by 20% \n"
@@ -198,49 +189,50 @@
"-- PRIMARY KEY (iguid, ipath) \n"
") INHERITS (gncKVPvalue); \n"
" \n"
"-- CREATE INDEX gncKVPvalue_int64_iguid_idx ON gncKVPvalue_int64 (iguid); \n"
" \n"
"CREATE TABLE gncKVPvalue_dbl ( \n"
" data FLOAT8 \n"
"-- PRIMARY KEY (iguid, ipath) \n"
") INHERITS (gncKVPvalue); \n"
" \n"
"-- CREATE INDEX gncKVPvalue_dbl_iguid_idx ON gncKVPvalue_dbl (iguid); \n"
" \n"
"CREATE TABLE gncKVPvalue_numeric ( \n"
" num INT8, \n"
" denom INT8 \n"
"-- PRIMARY KEY (iguid, ipath) \n"
") INHERITS (gncKVPvalue); \n"
" \n"
"-- CREATE INDEX gncKVPvalue_numeric_iguid_idx ON gncKVPvalue_numeric (iguid); \n"
" \n"
"CREATE TABLE gncKVPvalue_str ( \n"
" data TEXT \n"
"-- PRIMARY KEY (iguid, ipath) \n"
") INHERITS (gncKVPvalue); \n"
" \n"
"-- CREATE INDEX gncKVPvalue_str_iguid_idx ON gncKVPvalue_str (iguid); \n"
" \n"
"CREATE TABLE gncKVPvalue_guid ( \n"
" data CHAR(32) \n"
"-- PRIMARY KEY (iguid, ipath) \n"
") INHERITS (gncKVPvalue); \n"
" \n"
"-- CREATE INDEX gncKVPvalue_guid_iguid_idx ON gncKVPvalue_guid (iguid); \n"
" \n"
"CREATE TABLE gncKVPvalue_timespec ( \n"
" data TIMESTAMP \n"
" data TIMESTAMP WITH TIME ZONE \n"
"-- PRIMARY KEY (iguid, ipath) \n"
") INHERITS (gncKVPvalue); \n"
" \n"
"-- CREATE INDEX gncKVPvalue_timespec_iguid_idx ON gncKVPvalue_timespec (iguid); \n"
" \n"
"CREATE TABLE gncKVPvalue_list ( \n"
" data TEXT[] \n"
"-- PRIMARY KEY (iguid, ipath) \n"
") INHERITS (gncKVPvalue); \n"
" \n"
"-- CREATE INDEX gncAccount_pg_idx ON gncAccount (parentGuid); \n"
"CREATE INDEX gncTransaction_posted_idx ON gncTransaction (date_posted); \n"
"CREATE INDEX gncSplit_acc_idx ON gncSplit (accountGuid); \n"
"CREATE INDEX gncSplit_trn_idx ON gncSplit (transGuid); \n"
"--"
"-- The indexes are commented out due to the performance degradation\n"
"-- they cause, based on the inheritance problems\n"
"-- CREATE INDEX gncKVPvalue_iguid_idx ON gncKVPvalue (iguid); \n"
"-- CREATE INDEX gncKVPvalue_int64_iguid_idx ON gncKVPvalue_int64 (iguid); \n"
"-- CREATE INDEX gncKVPvalue_dbl_iguid_idx ON gncKVPvalue_dbl (iguid); \n"
"-- CREATE INDEX gncKVPvalue_numeric_iguid_idx ON gncKVPvalue_numeric (iguid); \n"
"-- CREATE INDEX gncKVPvalue_str_iguid_idx ON gncKVPvalue_str (iguid); \n"
"-- CREATE INDEX gncKVPvalue_guid_iguid_idx ON gncKVPvalue_guid (iguid); \n"
"-- CREATE INDEX gncKVPvalue_timespec_iguid_idx ON gncKVPvalue_timespec (iguid); \n"
"-- CREATE INDEX gncKVPvalue_list_iguid_idx ON gncKVPvalue_list (iguid); \n"
" \n"
"-- end of file";

View File

@@ -9,11 +9,13 @@
"-- work under Postgres 7.0 \n"
"-- \n"
"-- HISTORY: \n"
"-- Copyright (C) 2001 Linux Developers Group \n"
"-- Copyright (C) 2001 Linas Vepstas <linas@linas.org>\n"
"-- \n"
" \n"
"INSERT INTO gncVersion (major,minor,rev,name) VALUES (1,0,0,'Version Table'); \n"
"INSERT INTO gncVersion (major,minor,rev,name) VALUES (1,1,1,'iGUID in Main Tables'); \n"
"INSERT INTO gncVersion (major,minor,rev,name) VALUES (1,2,1,'Fix gncSubtotalReconedBalance'); \n"
"INSERT INTO gncVersion (major,minor,rev,name) VALUES (1,3,1,'Add kvp_timespec tables'); \n"
"INSERT INTO gncVersion (major,minor,rev,name) VALUES (1,4,1,'Add support for multiple books');";
"INSERT INTO gncVersion (major,minor,rev,name) VALUES (1,4,1,'Add support for multiple books');"
"INSERT INTO gncVersion (major, minor, rev, name, date) VALUES (1, 5, 1, 'Change timestamp definition', now());\n";

View File

@@ -25,7 +25,7 @@ define(`book', `gncBook, Book, QofBook, b,
bookGUID, KEY, GUID *, qof_book_get_guid(ptr),
')
define(`split', `gncEntry, Split, Split, e,
define(`split', `gncSplit, Split, Split, e,
accountGUID, , GUID *, xaccAccountGetGUID(xaccSplitGetAccount(ptr)),
transGUID, , GUID *, xaccTransGetGUID(xaccSplitGetParent(ptr)),
memo, , char *, xaccSplitGetMemo(ptr),
@@ -35,7 +35,7 @@ define(`split', `gncEntry, Split, Split, e,
amount, , int64, gnc_numeric_num(xaccSplitGetAmount(ptr)),
value, , int64, gnc_numeric_num(xaccSplitGetValue(ptr)),
iguid, , int32, ptr->idata,
entryGUID, KEY, GUID *, xaccSplitGetGUID(ptr),
splitGuid, KEY, GUID *, xaccSplitGetGUID(ptr),
')
/* note that for the last_modified, we use the sql database */

View File

@@ -6,11 +6,11 @@ EXIT_VALUE=0
# gdb .libs/test-db
rm -f test_file_*
if ./db-control.sh create; then
./db-control.sh start
if ${srcdir}/db-control.sh create; then
${srcdir}/db-control.sh start
./test-db localhost 7777 || EXIT_VALUE=1
./db-control.sh stop
./db-control.sh destroy
${srcdir}/db-control.sh stop
${srcdir}/db-control.sh destroy
elif [ "${PGHOST}X" != "X" ]; then
# This expects the logged in user to have authority
# to create databases.

View File

@@ -254,7 +254,7 @@ mark_transaction_commodities(Transaction * t, void *data)
g_hash_table_insert(hash, xaccTransGetCurrency(t), hash);
return TRUE;
return 0;
}
static gboolean

View File

@@ -115,7 +115,7 @@ delete_list_cb (PGBackend *be, PGresult *result, int j, gpointer data)
GList * deletelist = (GList *) data;
GUID guid = nullguid;
string_to_guid (DB_GET_VAL ("entryGuid", j), &guid);
string_to_guid (DB_GET_VAL ("splitGuid", j), &guid);
/* If the database has splits that the engine doesn't,
* collect 'em up & we'll have to delete em */
@@ -126,7 +126,7 @@ delete_list_cb (PGBackend *be, PGresult *result, int j, gpointer data)
dti = g_new (DeleteTransInfo, 1);
dti->guid = guid;
dti->guid_str = g_strdup (DB_GET_VAL ("entryGuid", j));
dti->guid_str = g_strdup (DB_GET_VAL ("splitGuid", j));
dti->iguid = atoi (DB_GET_VAL ("iguid", j));
deletelist = g_list_prepend (deletelist, dti);
@@ -157,7 +157,7 @@ pgendStoreTransactionNoLock (PGBackend *be, Transaction *trans,
* since what is there may not match what we have cached in
* the engine. */
p = be->buff; *p = 0;
p = stpcpy (p, "SELECT entryGuid, iguid FROM gncEntry WHERE transGuid='");
p = stpcpy (p, "SELECT splitGuid, iguid FROM gncSplit WHERE transGuid='");
p = guid_to_string_buff(xaccTransGetGUID(trans), p);
p = stpcpy (p, "';");
@@ -185,7 +185,7 @@ pgendStoreTransactionNoLock (PGBackend *be, Transaction *trans,
}
}
p = stpcpy (p, "DELETE FROM gncEntry WHERE entryGuid='");
p = stpcpy (p, "DELETE FROM gncSplit WHERE splitGuid='");
p = stpcpy (p, dti->guid_str);
p = stpcpy (p, "';\n");
}
@@ -265,7 +265,7 @@ pgendStoreTransactionNoLock (PGBackend *be, Transaction *trans,
{
Split * s = node->data;
pgendStoreAuditSplit (be, s, SQL_DELETE);
p = stpcpy (p, "DELETE FROM gncEntry WHERE entryGuid='");
p = stpcpy (p, "DELETE FROM gncSplit WHERE splitGuid='");
p = guid_to_string_buff (xaccSplitGetGUID(s), p);
p = stpcpy (p, "';\n");
}
@@ -311,7 +311,7 @@ pgendStoreTransaction (PGBackend *be, Transaction *trans)
/* lock it up so that we store atomically */
bufp = "BEGIN;\n"
"LOCK TABLE gncTransaction IN EXCLUSIVE MODE;\n"
"LOCK TABLE gncEntry IN EXCLUSIVE MODE;\n";
"LOCK TABLE gncSplit IN EXCLUSIVE MODE;\n";
SEND_QUERY (be,bufp, );
FINISH_QUERY(be->connection);
@@ -364,7 +364,7 @@ pgendStoreAllTransactions (PGBackend *be, AccountGroup *grp)
/* lock it up so that we store atomically */
p = "BEGIN;\n"
"LOCK TABLE gncTransaction IN EXCLUSIVE MODE;\n"
"LOCK TABLE gncEntry IN EXCLUSIVE MODE;\n";
"LOCK TABLE gncSplit IN EXCLUSIVE MODE;\n";
SEND_QUERY (be,p, );
FINISH_QUERY(be->connection);
@@ -441,7 +441,7 @@ pgendCopySplitsToEngine (PGBackend *be, Transaction *trans)
pbuff = be->buff;
pbuff[0] = 0;
pbuff = stpcpy (pbuff,
"SELECT * FROM gncEntry WHERE transGuid='");
"SELECT * FROM gncSplit WHERE transGuid='");
pbuff = guid_to_string_buff(trans_guid, pbuff);
pbuff = stpcpy (pbuff, "';");
@@ -467,9 +467,9 @@ pgendCopySplitsToEngine (PGBackend *be, Transaction *trans)
/* --------------------------------------------- */
/* first, lets see if we've already got this one */
PINFO ("split GUID=%s", DB_GET_VAL("entryGUID",j));
PINFO ("split GUID=%s", DB_GET_VAL("splitGuid",j));
guid = nullguid; /* just in case the read fails ... */
string_to_guid (DB_GET_VAL("entryGUID",j), &guid);
string_to_guid (DB_GET_VAL("splitGuid",j), &guid);
s = pgendSplitLookup (be, &guid);
if (!s)
{
@@ -937,7 +937,7 @@ pgend_trans_commit_edit (QofBackend * bend,
/* lock it up so that we query and store atomically */
bufp = "BEGIN;\n"
"LOCK TABLE gncTransaction IN EXCLUSIVE MODE;\n"
"LOCK TABLE gncEntry IN EXCLUSIVE MODE;\n";
"LOCK TABLE gncSplit IN EXCLUSIVE MODE;\n";
SEND_QUERY (be,bufp, );
FINISH_QUERY(be->connection);

View File

@@ -138,9 +138,9 @@ get_mass_entry_cb (PGBackend *be, PGresult *result, int j, gpointer data)
FIND_BOOK (book);
/* --------------------------------------------- */
PINFO ("split GUID=%s", DB_GET_VAL("entryGUID",j));
PINFO ("split GUID=%s", DB_GET_VAL("splitGuid",j));
guid = nullguid; /* just in case the read fails ... */
string_to_guid (DB_GET_VAL("entryGUID",j), &guid);
string_to_guid (DB_GET_VAL("splitGuid",j), &guid);
s = xaccSplitLookup (&guid, book);
if (!s)
{
@@ -167,7 +167,7 @@ get_mass_entry_cb (PGBackend *be, PGresult *result, int j, gpointer data)
"\t(split with guid=%s\n"
"\twants a trans with guid=%s\n"
"\tin book with guid=%s)\n",
DB_GET_VAL("entryGUID",j),
DB_GET_VAL("splitGuid",j),
DB_GET_VAL("transGUID",j),
DB_GET_VAL("bookGUID",j)
);
@@ -188,7 +188,7 @@ get_mass_entry_cb (PGBackend *be, PGresult *result, int j, gpointer data)
"\t(split with guid=%s\n"
"\twants an acct with guid=%s\n"
"\tin book with guid=%s)\n",
DB_GET_VAL("entryGUID",j),
DB_GET_VAL("splitGuid",j),
DB_GET_VAL("accountGUID",j),
DB_GET_VAL("bookGUID",j)
);
@@ -231,9 +231,9 @@ pgendGetMassTransactions (PGBackend *be, QofBook *book)
* a bookguid to the transaction table */
p = buff;
p = stpcpy (p, "SELECT DISTINCT gncTransaction.*, gncAccount.bookGuid as bookGuid "
" FROM gncTransaction, gncEntry, gncAccount "
" WHERE gncTransaction.transGuid = gncEntry.transGuid AND "
" gncEntry.accountGuid = gncAccount.accountGuid AND "
" FROM gncTransaction, gncSplit, gncAccount "
" WHERE gncTransaction.transGuid = gncSplit.transGuid AND "
" gncSplit.accountGuid = gncAccount.accountGuid AND "
" gncAccount.bookGuid = '");
p = guid_to_string_buff(qof_book_get_guid (book), p);
p = stpcpy (p, "';");
@@ -248,9 +248,9 @@ pgendGetMassTransactions (PGBackend *be, QofBook *book)
xaction_list = be->tmp_return;
p = buff;
p = stpcpy (p, "SELECT gncEntry.*, gncAccount.bookGuid as bookGuid "
" FROM gncEntry, gncAccount "
" WHERE gncEntry.accountGuid = gncAccount.accountGuid AND "
p = stpcpy (p, "SELECT gncSplit.*, gncAccount.bookGuid as bookGuid "
" FROM gncSplit, gncAccount "
" WHERE gncSplit.accountGuid = gncAccount.accountGuid AND "
" gncAccount.bookGuid = '");
p = guid_to_string_buff(qof_book_get_guid (book), p);
p = stpcpy (p, "';");

View File

@@ -27,8 +27,11 @@
#include <libpq-fe.h>
#include <stdlib.h>
#include <glib.h>
#include "PostgresBackend.h"
#include "messages.h"
#include "qofbackend.h"
#include "upgrade.h"
#include "putil.h"
@@ -38,7 +41,7 @@ static short module = MOD_BACKEND;
/* ============================================================= */
#define PGEND_CURRENT_MAJOR_VERSION 1
#define PGEND_CURRENT_MINOR_VERSION 4
#define PGEND_CURRENT_MINOR_VERSION 5
#define PGEND_CURRENT_REV_VERSION 1
/* ============================================================= */
@@ -134,6 +137,7 @@ put_iguid_in_tables (PGBackend *be)
char *p, buff[200];
guint iguid;
execQuery(be, "BEGIN");
p = "LOCK TABLE gncAccount IN ACCESS EXCLUSIVE MODE;\n"
"LOCK TABLE gncEntry IN ACCESS EXCLUSIVE MODE;\n"
"LOCK TABLE gncTransaction IN ACCESS EXCLUSIVE MODE;\n"
@@ -218,6 +222,8 @@ put_iguid_in_tables (PGBackend *be)
" (1,1,1,'End Put iGUID in Main Tables');";
SEND_QUERY (be,p, );
FINISH_QUERY(be->connection);
execQuery(be, "COMMIT");
}
/* ============================================================= */
@@ -227,6 +233,8 @@ fix_reconciled_balance_func (PGBackend *be)
{
char *p;
execQuery(be, "BEGIN");
p = "LOCK TABLE gncVersion IN ACCESS EXCLUSIVE MODE;\n "
"INSERT INTO gncVersion (major,minor,rev,name) VALUES \n"
" (1,2,0,'Start Fix gncSubtotalReconedBalance');";
@@ -257,6 +265,7 @@ fix_reconciled_balance_func (PGBackend *be)
" (1,2,1,'End Fix gncSubtotalReconedBalance');";
SEND_QUERY (be,p, );
FINISH_QUERY(be->connection);
execQuery(be, "COMMIT");
}
/* ============================================================= */
@@ -266,6 +275,8 @@ add_kvp_timespec_tables (PGBackend *be)
{
char *p;
execQuery(be, "BEGIN");
p = "LOCK TABLE gncVersion IN ACCESS EXCLUSIVE MODE;\n "
"INSERT INTO gncVersion (major,minor,rev,name) VALUES \n"
" (1,3,0,'Start Add kvp_timespec tables');";
@@ -291,6 +302,8 @@ add_kvp_timespec_tables (PGBackend *be)
" (1,3,1,'End Add kvp_timespec tables');";
SEND_QUERY (be,p, );
FINISH_QUERY(be->connection);
execQuery(be, "COMMIT");
}
/* ============================================================= */
@@ -298,8 +311,11 @@ add_kvp_timespec_tables (PGBackend *be)
static void
add_multiple_book_support (PGBackend *be)
{
char buff[4000];
char *p;
gchar *buff;
gchar *p;
const gchar *guid;
execQuery(be, "BEGIN");
p = "LOCK TABLE gncAccount IN ACCESS EXCLUSIVE MODE;\n"
"LOCK TABLE gncAccountTrail IN ACCESS EXCLUSIVE MODE;\n"
@@ -330,47 +346,185 @@ add_multiple_book_support (PGBackend *be)
SEND_QUERY (be,p, );
FINISH_QUERY(be->connection);
p = "ALTER TABLE gncAccount ADD COLUMN bookGuid CHAR(32) NOT NULL;\n"
"ALTER TABLE gncAccountTrail ADD COLUMN bookGuid CHAR(32) NOT NULL;\n"
"ALTER TABLE gncPrice ADD COLUMN bookGuid CHAR(32) NOT NULL;\n"
"ALTER TABLE gncPriceTrail ADD COLUMN bookGuid CHAR(32) NOT NULL;\n";
p = "ALTER TABLE gncAccount ADD COLUMN bookGuid CHAR(32);\n"
"ALTER TABLE gncAccountTrail ADD COLUMN bookGuid CHAR(32);\n"
"ALTER TABLE gncPrice ADD COLUMN bookGuid CHAR(32);\n"
"ALTER TABLE gncPriceTrail ADD COLUMN bookGuid CHAR(32);\n";
SEND_QUERY (be,p, );
FINISH_QUERY(be->connection);
p = buff;
p = stpcpy (p, "UPDATE gncAccount SET bookGuid = '");
p = guid_to_string_buff (qof_book_get_guid (be->book), p);
p = stpcpy (p, "';\n");
p = stpcpy (p, "UPDATE gncAccountTrail SET bookGuid = '");
p = guid_to_string_buff (qof_book_get_guid (be->book), p);
p = stpcpy (p, "';\n");
guid = guid_to_string(qof_book_get_guid(pgendGetBook(be)));
PINFO("guid = %s", guid);
buff = g_strdup_printf("UPDATE gncAccount SET bookGuid = '%s';\n"
"UPDATE gncAccountTrail SET bookGuid = '%s';\n"
"UPDATE gncPrice SET bookGuid = '%s';\n"
"UPDATE gncPriceTrail SET bookGuid = '%s';\n",
guid, guid, guid, guid);
SEND_QUERY (be,buff, );
FINISH_QUERY(be->connection);
p = buff;
p = stpcpy (p, "UPDATE gncPrice SET bookGuid = '");
p = guid_to_string_buff (qof_book_get_guid (be->book), p);
p = stpcpy (p, "';\n");
p = stpcpy (p, "UPDATE gncPriceTrail SET bookGuid = '");
p = guid_to_string_buff (qof_book_get_guid (be->book), p);
p = stpcpy (p, "';\n");
g_free(buff);
buff = g_strdup_printf("INSERT INTO gncBook (bookGuid, book_open, version, iguid) "
"VALUES ('%s', 'y', 1, 0);", guid);
SEND_QUERY (be,buff, );
FINISH_QUERY(be->connection);
p = buff;
p = stpcpy (p, "INSERT INTO gncBook (bookGuid, book_open, version, iguid) "
"VALUES ('");
p = guid_to_string_buff (qof_book_get_guid (be->book), p);
p = stpcpy (p, "', 'y', 1, 0);");
SEND_QUERY (be,buff, );
g_free(buff);
p = "ALTER TABLE gncAccount ALTER COLUMN bookGuid SET NOT NULL;\n"
"ALTER TABLE gncAccountTrail ALTER COLUMN bookGuid SET NOT NULL;\n"
"ALTER TABLE gncPrice ALTER COLUMN bookGuid SET NOT NULL;\n"
"ALTER TABLE gncPriceTrail ALTER COLUMN bookGuid SET NOT NULL;\n";
SEND_QUERY (be,p, );
FINISH_QUERY(be->connection);
p = "INSERT INTO gncVersion (major,minor,rev,name) VALUES \n"
" (1,4,1,'End Add multiple book support');";
SEND_QUERY (be,p, );
FINISH_QUERY(be->connection);
execQuery(be, "COMMIT");
}
static void
add_timezone_support(PGBackend *be) {
#include "newtables.h"
ENTER(" ");
if (!be) { LEAVE("Backend is (null)"); return; }
if (!be->connection) {
qof_backend_set_message(&be->be, _("Backend connection is not available"));
qof_backend_set_error(&be->be, ERR_BACKEND_CONN_LOST);
return;
}
if (execQuery(be, "BEGIN WORK;\n") != PGRES_COMMAND_OK) {
LEAVE("Failed at BEGIN WORK (1)");
return;
}
/* Drop the _old tables if the exist already from a previous
* upgrage attempt, although
* the ROLLBACKs ought to ensure that they don't
*/
execQuery(be, drop_old_tables);
execQuery(be, "COMMIT");
/* Clear backend error messages. Use _get_error, because
* _set_error won't clear the backend error message if it
* is already set.
*/
qof_backend_set_message(&be->be, NULL);
qof_backend_get_error(&be->be);
/* execQuery sets the backend error message if one occurs */
if (execQuery(be, "BEGIN WORK") != PGRES_COMMAND_OK) {
LEAVE("Failed at BEGIN WORK (2)");
return;
}
if (execQuery(be, lock_tables) != PGRES_COMMAND_OK) {
execQuery(be, "ROLLBACK");
LEAVE("Failed at lock tables");
return;
}
if (execQuery(be, lock_entry) != PGRES_COMMAND_OK) {
execQuery(be, "ROLLBACK");
LEAVE("Failed at lock entry tables");
return;
}
if (execQuery(be, drop_index) != PGRES_COMMAND_OK) {
execQuery(be, "ROLLBACK");
LEAVE("Failed at drop indexes");
return;
}
if (execQuery(be, drop_functions) != PGRES_COMMAND_OK) {
execQuery(be, "ROLLBACK");
LEAVE("Failed at drop functions");
return;
}
if (execQuery(be, alter_tables) != PGRES_COMMAND_OK) {
execQuery(be, "ROLLBACK");
LEAVE("Failed at alter tables");
return;
}
if (execQuery(be, create_new_tables) != PGRES_COMMAND_OK) {
execQuery(be, "ROLLBACK");
LEAVE("Failed at create new tables");
return;
}
if (execQuery(be, create_audits) != PGRES_COMMAND_OK) {
execQuery(be, "ROLLBACK");
LEAVE("Failed at create audit tables");
return;
}
if (execQuery(be, create_indexes) != PGRES_COMMAND_OK) {
execQuery(be, "ROLLBACK");
LEAVE("Failed at create indexes");
return;
}
if (execQuery(be, create_functions) != PGRES_COMMAND_OK) {
execQuery(be, "ROLLBACK");
LEAVE("Failed at create functions");
return;
}
if (execQuery(be, lock_tables) != PGRES_COMMAND_OK) {
execQuery(be, "ROLLBACK");
LEAVE("Failed at lock tables (2)");
return;
}
if (execQuery(be, lock_split) != PGRES_COMMAND_OK) {
execQuery(be, "ROLLBACK");
LEAVE("Failed at lock split");
return;
}
if (execQuery(be, insert_new_data) != PGRES_COMMAND_OK) {
execQuery(be, "ROLLBACK");
LEAVE("Failed at insert new data");
return;
}
if (execQuery(be, version_sql) != PGRES_COMMAND_OK) {
execQuery(be, "ROLLBACK");
LEAVE("Failed at insert version row");
return;
}
/* Everything worked thus far, commit it now! */
execQuery(be, "COMMIT WORK");
/* Clean up crud, Drop the _old tables, vacuum to
* bring the indexes into use.
*/
execQuery(be, "BEGIN WORK");
execQuery(be, drop_old_tables);
execQuery(be, "COMMIT WORK");
execQuery(be, "VACUUM FULL ANALYZE");
/* Clear backend error messages. Use _get_error, because
* _set_error won't clear the backend error message if it
* is already set.
*
* Cleanup failures don't necessarily mean we should error
* out, but it would be handy to have an informational
* message to pass back to the user...
*/
qof_backend_set_message(&be->be, NULL);
qof_backend_get_error(&be->be);
LEAVE("Success");
}
/* ============================================================= */
/* Are we up to date ? */
/* Return 0 if we are at db version. Return +1 if we are newer.
@@ -414,25 +568,28 @@ pgendUpgradeDB (PGBackend *be)
vers = pgendGetVersion(be);
/* start adding features to bring database up to date */
if (1 == vers.major)
if (vers.major == 1)
{
/* version 1.1.0 add iguids to transaction and entry tables */
if (1 > vers.minor)
if (vers.minor < 1)
{
put_iguid_in_tables(be);
}
if (2 > vers.minor)
if (vers.minor < 2)
{
fix_reconciled_balance_func (be);
}
if (3 > vers.minor)
if (vers.minor < 3)
{
add_kvp_timespec_tables (be);
}
if (4 > vers.minor)
if (vers.minor < 4)
{
add_multiple_book_support (be);
}
if (vers.minor < 5) {
add_timezone_support(be);
}
}
}

View File

@@ -673,12 +673,11 @@ static gboolean add_to_verslist (struct _listinfo *listinfo,
return TRUE;
}
static gboolean add_txnvers_cb (Transaction *t, void *data)
static gint add_txnvers_cb (Transaction *t, void *data)
{
if (!t || !data)
return FALSE;
return add_to_verslist ((struct _listinfo *)data, &(t->guid), t->version);
if (!t || !data) return 1;
add_to_verslist ((struct _listinfo *)data, &(t->guid), t->version);
return 0;
}
gnc_vers_list * rpcend_build_gncverslist_txn (GList *txnlist,

View File

@@ -14,6 +14,7 @@ EXTRA_DIST = \
gnucash-build-env.in \
gnucash-make-guids \
gnucash-run-script \
gnucash-valgrind \
guile.in
noinst_DATA = gnucash-build-env

View File

@@ -1,4 +1,5 @@
SUBDIRS = . test file
PWD := $(shell pwd)
pkglib_LTLIBRARIES = libgw-business-core.la libgncmod-business-core.la

View File

@@ -58,6 +58,7 @@ const gchar *address_version_string = "2.0.0";
#define addr_phone_string "addr:phone"
#define addr_fax_string "addr:fax"
#define addr_email_string "addr:email"
#define addr_slots_string "addr:slots"
static void
maybe_add_string (xmlNodePtr ptr, const char *tag, const char *str)
@@ -173,6 +174,12 @@ address_email_handler (xmlNodePtr node, gpointer addr_pdata)
return set_string(node, pdata->address, gncAddressSetEmail);
}
static gboolean
address_slots_handler (xmlNodePtr node, gpointer addr_pdata)
{
return TRUE;
}
static struct dom_tree_handler address_handlers_v2[] = {
{ addr_name_string, address_name_handler, 0, 0 },
{ addr_addr1_string, address_addr1_handler, 0, 0 },
@@ -182,6 +189,7 @@ static struct dom_tree_handler address_handlers_v2[] = {
{ addr_phone_string, address_phone_handler, 0, 0 },
{ addr_fax_string, address_fax_handler, 0, 0 },
{ addr_email_string, address_email_handler, 0, 0 },
{ addr_slots_string, address_slots_handler, 0, 0 },
{ NULL, 0, 0, 0 }
};

View File

@@ -63,6 +63,7 @@ const gchar *billterm_version_string = "2.0.0";
#define billterm_invisible_string "billterm:invisible"
#define billterm_parent_string "billterm:parent"
#define billterm_child_string "billterm:child"
#define billterm_slots_string "billterm:slots"
#define gnc_daystype_string "billterm:days"
#define days_duedays_string "bt-days:due-days"
@@ -403,6 +404,12 @@ billterm_prox_data_handler (xmlNodePtr node, gpointer billterm_pdata)
return dom_tree_to_prox_data (node, pdata);
}
static gboolean
billterm_slots_handler (xmlNodePtr node, gpointer billterm_pdata)
{
return TRUE;
}
static struct dom_tree_handler billterm_handlers_v2[] = {
{ billterm_guid_string, billterm_guid_handler, 1, 0 },
{ billterm_name_string, billterm_name_handler, 1, 0 },
@@ -411,6 +418,7 @@ static struct dom_tree_handler billterm_handlers_v2[] = {
{ billterm_invisible_string, billterm_invisible_handler, 1, 0 },
{ billterm_parent_string, billterm_parent_handler, 0, 0 },
{ billterm_child_string, billterm_child_handler, 0, 0 },
{ billterm_slots_string, billterm_slots_handler, 0, 0 },
{ gnc_daystype_string, billterm_days_data_handler, 0, 0 },
{ gnc_proximotype_string, billterm_prox_data_handler, 0, 0 },
{ NULL, 0, 0, 0 }

View File

@@ -72,6 +72,7 @@ const gchar *customer_version_string = "2.0.0";
#define cust_currency_string "cust:currency"
#define cust_taxtable_string "cust:taxtable"
#define cust_taxtableoverride_string "cust:use-tt"
#define cust_slots_string "cust:slots"
static void
maybe_add_string (xmlNodePtr ptr, const char *tag, const char *str)
@@ -367,6 +368,12 @@ customer_taxtableoverride_handler (xmlNodePtr node, gpointer cust_pdata)
return set_boolean (node, pdata->customer, gncCustomerSetTaxTableOverride);
}
static gboolean
customer_slots_handler (xmlNodePtr node, gpointer cust_pdata)
{
return TRUE;
}
static struct dom_tree_handler customer_handlers_v2[] = {
{ cust_name_string, customer_name_handler, 1, 0 },
{ cust_guid_string, customer_guid_handler, 1, 0 },
@@ -383,6 +390,7 @@ static struct dom_tree_handler customer_handlers_v2[] = {
{ "cust:commodity", customer_currency_handler, 0, 0 }, /* XXX */
{ cust_taxtable_string, customer_taxtable_handler, 0, 0 },
{ cust_taxtableoverride_string, customer_taxtableoverride_handler, 0, 0 },
{ cust_slots_string, customer_slots_handler, 0, 0 },
{ NULL, 0, 0, 0 }
};

View File

@@ -67,6 +67,7 @@ const gchar *employee_version_string = "2.0.0";
#define employee_rate_string "employee:rate"
#define employee_currency_string "employee:currency"
#define employee_ccard_string "employee:ccard"
#define employee_slots_string "employee:slots"
static void
maybe_add_string (xmlNodePtr ptr, const char *tag, const char *str)
@@ -285,6 +286,12 @@ employee_ccard_handler (xmlNodePtr node, gpointer employee_pdata)
return TRUE;
}
static gboolean
employee_slots_handler (xmlNodePtr node, gpointer employee_pdata)
{
return TRUE;
}
static struct dom_tree_handler employee_handlers_v2[] = {
{ employee_username_string, employee_username_handler, 1, 0 },
{ employee_guid_string, employee_guid_handler, 1, 0 },
@@ -298,6 +305,7 @@ static struct dom_tree_handler employee_handlers_v2[] = {
{ employee_currency_string, employee_currency_handler, 0, 0 }, /* XXX */
{ "employee:commodity", employee_currency_handler, 0, 0 }, /* XXX */
{ employee_ccard_string, employee_ccard_handler, 0, 0 },
{ employee_slots_string, employee_slots_handler, 0, 0 },
{ NULL, 0, 0, 0 }
};

View File

@@ -93,6 +93,7 @@ const gchar *entry_version_string = "2.0.0";
#define entry_order_string "entry:order"
#define entry_invoice_string "entry:invoice"
#define entry_bill_string "entry:bill"
#define entry_slots_string "entry:slots"
static void
maybe_add_string (xmlNodePtr ptr, const char *tag, const char *str)
@@ -223,7 +224,7 @@ struct entry_pdata
Account *acc;
};
static gboolean
static inline gboolean
set_string(xmlNodePtr node, GncEntry* entry,
void (*func)(GncEntry *entry, const char *txt))
{
@@ -235,19 +236,18 @@ set_string(xmlNodePtr node, GncEntry* entry,
return TRUE;
}
static gboolean
static inline gboolean
set_timespec(xmlNodePtr node, GncEntry* entry,
void (*func)(GncEntry *entry, Timespec ts))
{
Timespec *ts = dom_tree_to_timespec (node);
g_return_val_if_fail(ts, FALSE);
Timespec ts = dom_tree_to_timespec (node);
g_return_val_if_fail(is_valid_timespec(ts), FALSE);
func(entry, *ts);
g_free(ts);
func(entry, ts);
return TRUE;
}
static gboolean
static inline gboolean
set_numeric(xmlNodePtr node, GncEntry* entry,
void (*func)(GncEntry *entry, gnc_numeric num))
{
@@ -259,7 +259,7 @@ set_numeric(xmlNodePtr node, GncEntry* entry,
return TRUE;
}
static gboolean
static inline gboolean
set_boolean(xmlNodePtr node, GncEntry* entry,
void (*func)(GncEntry *entry, gboolean val))
{
@@ -271,7 +271,7 @@ set_boolean(xmlNodePtr node, GncEntry* entry,
return TRUE;
}
static gboolean
static inline gboolean
set_account(xmlNodePtr node, struct entry_pdata *pdata,
void (*func)(GncEntry *entry, Account *acc))
{
@@ -291,7 +291,7 @@ set_account(xmlNodePtr node, struct entry_pdata *pdata,
return TRUE;
}
static gboolean
static inline gboolean
set_taxtable (xmlNodePtr node, struct entry_pdata *pdata,
void (*func)(GncEntry *entry, GncTaxTable *taxtable))
{
@@ -651,6 +651,12 @@ entry_price_handler (xmlNodePtr node, gpointer entry_pdata)
return res;
}
static gboolean
entry_slots_handler (xmlNodePtr node, gpointer entry_pdata)
{
return TRUE;
}
static struct dom_tree_handler entry_handlers_v2[] = {
{ entry_guid_string, entry_guid_handler, 1, 0 },
{ entry_date_string, entry_date_handler, 1, 0 },
@@ -686,6 +692,7 @@ static struct dom_tree_handler entry_handlers_v2[] = {
{ entry_order_string, entry_order_handler, 0, 0 },
{ entry_invoice_string, entry_invoice_handler, 0, 0 },
{ entry_bill_string, entry_bill_handler, 0, 0 },
{ entry_slots_string, entry_slots_handler, 0, 0 },
/* Old XML support */
{ "entry:acct", entry_acct_handler, 0, 0 },

View File

@@ -72,6 +72,7 @@ const gchar *invoice_version_string = "2.0.0";
#define invoice_currency_string "invoice:currency"
#define invoice_billto_string "invoice:billto"
#define invoice_tochargeamt_string "invoice:charge-amt"
#define invoice_slots_string "invoice:slots"
static void
maybe_add_string (xmlNodePtr ptr, const char *tag, const char *str)
@@ -168,7 +169,7 @@ struct invoice_pdata
GNCBook *book;
};
static gboolean
static inline gboolean
set_string(xmlNodePtr node, GncInvoice* invoice,
void (*func)(GncInvoice *invoice, const char *txt))
{
@@ -181,20 +182,18 @@ set_string(xmlNodePtr node, GncInvoice* invoice,
return TRUE;
}
static gboolean
static inline gboolean
set_timespec(xmlNodePtr node, GncInvoice* invoice,
void (*func)(GncInvoice *invoice, Timespec ts))
{
Timespec* ts = dom_tree_to_timespec(node);
g_return_val_if_fail(ts, FALSE);
Timespec ts = dom_tree_to_timespec(node);
g_return_val_if_fail(is_valid_timespec(ts), FALSE);
func(invoice, *ts);
g_free(ts);
func(invoice, ts);
return TRUE;
}
static gboolean
static inline gboolean
invoice_guid_handler (xmlNodePtr node, gpointer invoice_pdata)
{
struct invoice_pdata *pdata = invoice_pdata;
@@ -400,6 +399,12 @@ invoice_tochargeamt_handler (xmlNodePtr node, gpointer invoice_pdata)
return TRUE;
}
static gboolean
invoice_slots_handler (xmlNodePtr node, gpointer invoice_pdata)
{
return TRUE;
}
static struct dom_tree_handler invoice_handlers_v2[] = {
{ invoice_guid_string, invoice_guid_handler, 1, 0 },
{ invoice_id_string, invoice_id_handler, 1, 0 },
@@ -417,6 +422,7 @@ static struct dom_tree_handler invoice_handlers_v2[] = {
{ "invoice:commodity", invoice_currency_handler, 0, 0 },
{ invoice_billto_string, invoice_billto_handler, 0, 0 },
{ invoice_tochargeamt_string, invoice_tochargeamt_handler, 0, 0},
{ invoice_slots_string, invoice_slots_handler, 0, 0 },
{ NULL, 0, 0, 0 }
};

View File

@@ -62,6 +62,7 @@ const gchar *job_version_string = "2.0.0";
#define job_reference_string "job:reference"
#define job_owner_string "job:owner"
#define job_active_string "job:active"
#define job_slots_string "job:slots"
static void
maybe_add_string (xmlNodePtr ptr, const char *tag, const char *str)
@@ -195,6 +196,12 @@ job_active_handler (xmlNodePtr node, gpointer job_pdata)
return ret;
}
static gboolean
job_slots_handler (xmlNodePtr node, gpointer job_pdata)
{
return TRUE;
}
static struct dom_tree_handler job_handlers_v2[] = {
{ job_guid_string, job_guid_handler, 1, 0 },
{ job_id_string, job_id_handler, 1, 0 },
@@ -202,6 +209,7 @@ static struct dom_tree_handler job_handlers_v2[] = {
{ job_reference_string, job_reference_handler, 0, 0 },
{ job_owner_string, job_owner_handler, 1, 0 },
{ job_active_string, job_active_handler, 1, 0 },
{ job_slots_string, job_slots_handler, 0, 0 },
{ NULL, 0, 0, 0 }
};

View File

@@ -64,6 +64,7 @@ const gchar *order_version_string = "2.0.0";
#define order_notes_string "order:notes"
#define order_reference_string "order:reference"
#define order_active_string "order:active"
#define order_slots_string "order:slots"
static void
maybe_add_string (xmlNodePtr ptr, const char *tag, const char *str)
@@ -114,7 +115,7 @@ struct order_pdata
GNCBook *book;
};
static gboolean
static inline gboolean
set_string(xmlNodePtr node, GncOrder* order,
void (*func)(GncOrder *order, const char *txt))
{
@@ -127,20 +128,18 @@ set_string(xmlNodePtr node, GncOrder* order,
return TRUE;
}
static gboolean
static inline gboolean
set_timespec(xmlNodePtr node, GncOrder* order,
void (*func)(GncOrder *order, Timespec ts))
{
Timespec* ts = dom_tree_to_timespec(node);
g_return_val_if_fail(ts, FALSE);
Timespec ts = dom_tree_to_timespec(node);
g_return_val_if_fail(is_valid_timespec(ts), FALSE);
func(order, *ts);
g_free(ts);
func(order, ts);
return TRUE;
}
static gboolean
static inline gboolean
order_guid_handler (xmlNodePtr node, gpointer order_pdata)
{
struct order_pdata *pdata = order_pdata;
@@ -231,6 +230,12 @@ order_active_handler (xmlNodePtr node, gpointer order_pdata)
return ret;
}
static gboolean
order_slots_handler (xmlNodePtr node, gpointer order_pdata)
{
return TRUE;
}
static struct dom_tree_handler order_handlers_v2[] = {
{ order_guid_string, order_guid_handler, 1, 0 },
{ order_id_string, order_id_handler, 1, 0 },
@@ -240,6 +245,7 @@ static struct dom_tree_handler order_handlers_v2[] = {
{ order_notes_string, order_notes_handler, 0, 0 },
{ order_reference_string, order_reference_handler, 0, 0 },
{ order_active_string, order_active_handler, 1, 0 },
{ order_slots_string, order_slots_handler, 0, 0 },
{ NULL, 0, 0, 0 }
};

View File

@@ -63,6 +63,7 @@ const gchar *taxtable_version_string = "2.0.0";
#define taxtable_parent_string "taxtable:parent"
#define taxtable_child_string "taxtable:child"
#define taxtable_entries_string "taxtable:entries"
#define taxtable_slots_string "taxtable:slots"
#define gnc_taxtableentry_string "gnc:GncTaxTableEntry"
#define ttentry_account_string "tte:acct"
@@ -357,6 +358,12 @@ taxtable_entries_handler (xmlNodePtr node, gpointer taxtable_pdata)
return TRUE;
}
static gboolean
taxtable_slots_handler (xmlNodePtr node, gpointer taxtable_pdata)
{
return TRUE;
}
static struct dom_tree_handler taxtable_handlers_v2[] = {
{ taxtable_guid_string, taxtable_guid_handler, 1, 0 },
{ taxtable_name_string, taxtable_name_handler, 1, 0 },
@@ -365,6 +372,7 @@ static struct dom_tree_handler taxtable_handlers_v2[] = {
{ taxtable_parent_string, taxtable_parent_handler, 0, 0 },
{ taxtable_child_string, taxtable_child_handler, 0, 0 },
{ taxtable_entries_string, taxtable_entries_handler, 1, 0 },
{ taxtable_slots_string, taxtable_slots_handler, 0, 0 },
{ NULL, 0, 0, 0 }
};

View File

@@ -69,6 +69,7 @@ const gchar *vendor_version_string = "2.0.0";
#define vendor_currency_string "vendor:currency"
#define vendor_taxtable_string "vendor:taxtable"
#define vendor_taxtableoverride_string "vendor:use-tt"
#define vendor_slots_string "vendor:slots"
static void
maybe_add_string (xmlNodePtr ptr, const char *tag, const char *str)
@@ -314,6 +315,12 @@ vendor_taxtableoverride_handler (xmlNodePtr node, gpointer vendor_pdata)
return set_boolean (node, pdata->vendor, gncVendorSetTaxTableOverride);
}
static gboolean
vendor_slots_handler (xmlNodePtr node, gpointer vendor_pdata)
{
return TRUE;
}
static struct dom_tree_handler vendor_handlers_v2[] = {
{ vendor_name_string, vendor_name_handler, 1, 0 },
{ vendor_guid_string, vendor_guid_handler, 1, 0 },
@@ -327,6 +334,7 @@ static struct dom_tree_handler vendor_handlers_v2[] = {
{ "vendor:commodity", vendor_currency_handler, 0, 0 }, /* XXX */
{ vendor_taxtable_string, vendor_taxtable_handler, 0, 0 },
{ vendor_taxtableoverride_string, vendor_taxtableoverride_handler, 0, 0 },
{ vendor_slots_string, vendor_slots_handler, 0, 0 },
{ NULL, 0, 0, 0 }
};

View File

@@ -1,4 +1,5 @@
SUBDIRS = . glade ui
PWD := $(shell pwd)
pkglib_LTLIBRARIES = libgncmod-business-gnome.la libgw-business-gnome.la

View File

@@ -1,3 +1,5 @@
PWD := $(shell pwd)
pkglib_LTLIBRARIES = libgncmod-dialog-tax-table.la libgw-dialog-tax-table.la
AM_CFLAGS = \

View File

@@ -3,8 +3,8 @@
--------------------------
Implementation Overview
Linas Vepstas December 2001
Last Updated July 2003
Linas Vepstas <linas@linas.org> December 2001
Last Updated August 2003
A top, unimplemented request for GnuCash is the ability to 'close
@@ -16,12 +16,24 @@ implementation design choices and the current design status.
Definition
----------
An accounting period or 'book' is a set of accounts and transactions
that, once closed, must never be modified again. Books are usually
that, once closed, must never be modified again. Books are typically
closed once a quarter, or once a year. Generating a report from a
closed book tells you how well you did for that year (or quarter).
The provision against modifying a closed book helps ensure correctness,
and discourages cheating. Note that the closing balances of a closed
book must be carried forward as the opening balance in the next book.
(But only for asset, liability and equity accounts. Opening balances
are zeroed for income and expense accounts.)
Note that the correct handling of depreciation, capital gains and
other similar accounting concepts requires 'Lots' to be correctly
handled. Lots are a way of identifying that an item bought in one
transaction is the same as that sold in another transaction.
When a book is closed, the entire lot must be brought forward,
and not just the account balance, because depreciation, capital
gains, taxes, etc. depend on the dates of the originating
transaction. See 'lots.txt' for details.
Requirements
------------
@@ -32,17 +44,13 @@ of multi-year data (i.e. create reports spanning multiple books).
Status
------
A fair amount of what is required to support books can be found in
the file src/engine/gnc-book.c. The actual implementation used is
"Plan A" from the list of choices below. See also "Implementation
Notes" at bottom for the current status.
Note that the correct handling of depreciation, capital gains and
other similar accounting concepts requires that 'lots' be supported
by the engine, and in turn, handled correctly by the 'books'
infrastructure. Currently, 'lots' are incompletely supported by
the engine, and this causes the book implementation to be broken in
a certain fundamental sense. See 'lots.txt' for details.
The top-level structure that holds references to all of the data in
a book is implemented in src/engine/qofbook.c. The routines to
split one book into two, automatically setting account balances,
etc. are implemented in src/engine/Period.c. The actual
implementation used is "Plan A" from the list of choices
below. The actual implementation is not yet complete, see
"Implementation Notes" at bottom for the current status.
Possible Solutions
@@ -56,7 +64,7 @@ this. More specifically: Split one file into two, with only 'old'
transactions in one, and only 'new' transactions in the other.
I believe that this can be 'easily' coded by creating a second instance
of a gnc-book structure in memory, copying all the account info
of a qofbook structure in memory, copying all the account info
into it, and using Query.c to copy in only the 'old' transactions.
(and v.v. using Query.c to delete the old transactions in the other copy.)
Then, look up the ending balance on all asset/liability accounts
@@ -66,7 +74,7 @@ The transfer description is, of course, 'opening balance'.
Balances of income/expense accounts are zeroed out.
I believe this code would be easy to write in C or scheme. There may be
a few bugs/difficulties lurking in gnc-book that might trip things up.
a few bugs/difficulties lurking in qofbook that might trip things up.
Also, at a minimum, there needs to be a GUI dialog, asking for the
date on which to close the books.
@@ -225,17 +233,64 @@ Pro's & Con's
-------------
I am not aware of any con's to plan A at this point.
Implementation Overview
----------------------
Plan A has been implemented in the engine. To quickly summarize:
-- Partitioning involves splitting one book into two, called the
"old, closing book", and the "current open book".
-- Accounts are copied between old and new books. One of the copies
is issued new GUID's, but the rest of the account data is copied.
KVP pairs are then added so that each copy points at the other,
and can thus be easily found. The "gemini" KVP keyword is used.
The opening balance is zeroed for income accounts. A transaction
is created to set the correct opening balance on asset accounts.
The transaction is a transfer from equity. An equity account is
created automagically, if needed.
-- Transactions. Transactions are partitioned, and end up either
in the old or the new book. Splits move with transactions.
Note that some transactions, associated with open lots, may be
kept in the new book (See below).
-- Lots. If a transaction has a split in an open lot, then that
transaction is not moved to the closed book. It needs to stay
with the open book. If a lot is closed, and all of the other
lots associated with all of the transactions in this lot are
also closed, then the lot may be moved to the closed book
(and all of the other associated lots must then also be moved).
-- Prices. Prices are sorted into the new and old books according
to the date on the price.
-- Scheduled transactions/recurring transactions. These are left
in the new book, untouched. They are not copied into the old
book, and no trace of their existence is left in the old book.
-- Business Objects. Not implemented.
Implementation Notes:
---------------------
Plan A has been implemented in the engine.
-- src/engine/Period.[ch]
Implements the main logic to split one book into two, and populate
it with the appropriate keys, markup, etc. and to carry balances
forward, etc.
src/engine/gemini
src/engine/kvp-util.[ch]
Gemini code: code which allows 'linked lists' to be created, using
nothing but kvp trees and guid's. These links are used to identify
peer accounts/ peer transactions, etc.
src/engine/gnc-lot.[ch]
Implements accounting Lots.
src/engine/Scrub2.[ch]
Implements simple FIFO for lots. Data is scrubbed with respect to
this FIFO before the books are closed. Commodity accounts (e.g.
stock accounts) must have a coherent Lots structure before books
can be closed. The scrubber makes sure there's nothing hanging
out.
src/gnome/druid-acct-period.[ch]
Implements a druid interface to allow user to specify book closing
@@ -247,37 +302,138 @@ Plan A has been implemented in the engine.
All books are stored in the same database; there is no performance
penalty for storing all books together in the same SQL database,
because they can be easily picked apart by an appropriate SQL query.
-- The xml-file backend can store multiple books, but there is currently
no support for spreading things out across multiple files (for
faster load, by not loading old books).
(Not tested, probably borken).
The current implementation of books is broken because it does not
yet make use of 'lots'. The problem is that for stock accounts (and
other non-currency accounts), it is not enough to supply merely the
opening balance. In order to handle depreciation or cap gains properly,
the dates and quantities of a purchase need to be easily available.
See "lots.txt" for details.
Period-closing does not delete anything from the SQL database. That's
the design. The core assumption is that having lots of old data in the
SQL DB wouldn't slow it down, and thus, nothing needed to be actually
deleted. So it doesn't delete anything.
However, an SQL admin, if they were desperate to trim the DB size,
could DELETE FROM * WHERE bookguid='asdfasdfasdf';
-- The XML-file backend can store multiple books in one file. There
is currently minimal support for writing out multiple books,
one per file (this enables faster load, by not loading old books).
There is currently no support for loading multiple books from multiple
files.
ANNOUNCE: Book Closing Beta 2
-----------------------------
Books AKA Accounting Periods can now be closed by going to the 'Action'
menu on the main window, and selecting 'close books'. This will popup
a druid that will allow you to select closing dates. Books are closed
one at a time: after each one, you can go back and alter the close date.
A book is 'closed' by moving transactions and prices earlier than the
indicated date to a separate file.
As a side-effect of book closing, capital gains (losses) will be
automatically computed using a FIFO accounting method. Stocks
or other non-cash assets that haven't been sold will remain in the
currently open book, even if the purchasing transactions were made
a long time ago.
The biggest drawback to the automatic computation of the gains/losses
is that there is no GUI to specify alternate accounting methods
(e.g. LIFO, or hand-picked lots, etc.) There is basically no practical
way to 'undo' the results of the gain/loss computations.
The other main drawback to book closing is that this will prevent
multi-year (multi-period) reports & graphs from being generated.
The old data will be in separate files, and there is currently no
way to load several files at once.
Open Issues/Questions that Need Discussion:
-------------------------------------------
*) How to handle business objects? e.g. vendors, customers should be
copied into both old and new books. But invoices should be in one
or the other. Need to document which is which.
*) Discussion Q: What should the naming convention be for the different
books? When the XML file backend is used, different books need to be
stored as different files (in order to avoid the performance
penalty of a large file load). Currently, what's implemented is
book-1dc750aa3e6fd045c13ac8afb1a9ac03-my-gnucash-file.xac.gml
where the number is the GUID of the closed book (needed to be able to
quickly find the book/file) and 'my-gnucash-file.xac' is the name of
the original file whose books were closed.
Need to change the name to include the word "archive".
*) Discussion Q: When saving books, make the book title part of the
book name (user convenience).
*) Should closed books be allowed to have unreconciled transactions?
Answer: probably. Should there be a warning?
Open Issues / ToDo
------------------
*) Naming convention for the different books? When the XML file backend
is used, different books need to be stored as different files (in order
to avoid the performance penalty of a large file load). This hasn't
been implemented yet ...
*) Change GUI to allow user to specify a default equity account
for dealing with opening balances. Should be done with kvp markup.
*) Special considerations for scheduled transactions/recurring transactions?
*) Fix crash when exiting gnucash after closing books.
*) The filename of the old, closed books should probably be saved
in the KVP tree of the current open book. This need be done only
from the file backend.
*) Need to mark closed book as unalterable, and respect that markup.
I think there's a 'closed' flag in the book, but I don't think its
respected.
*) The book closing GUI (druid-acct-period.c) needs to save/restore
period end date (the FreqSpec) to KVP on open book. This would be
easy once we have a freq-spec-to-kvp and freq-spec-from-kvp routines.
*) Handling of lots in book closing is implemented but is poorly tested.
Need to write test cases. Also test cases for prices in book
closing.
*) price-saving in the SQL backend is probably borken, its certainly
untested. Need to remove old deprecated price-lookup mechanism,
and replace w/ qofquery.
*) Need to provide for loading of closed books, because this is needed
for reports.
*) Handling of multi-book reports ??? Need to work out the recommended way
of making this happen....
*) Lots are now implemented in the engine, but not yet used in this code.
*) Have some way of remembering the quickfill text from older books.
*) Need to mark closed book as unalterable, and respect that markup.
*) Possibly neat idea:
Book closing, as currently implemented in the GUI, is driven entirely
by the date-posted. There is no (planned) interface to allow you to
exclude some certain transactions from a particular closing (although
it would be 'easy' to add this: right before I close a book, I have a
list of transactions which can be added to/removed from).
*) Need to trimming of price database
*) add 13-period support to FreqSpec and the FreqSpec widget.
e.g. from the mailing list:
One of the calendars my company uses is like this:
13 periods
1st period begins jan 1st, partial first week plus 4 weeks.
2nd - 13th period begins on a sunday every four weeks.
13th period - 4th week may be less than full week because it ends on 12/31.
For 2003:
01. 01/01 - 02/01
02. 02/02 - 03/01
03. 03/02 - 03/29
04. 03/30 - 04/26
05. 04/27 - 05/24
06. 05/25 - 06/21
07. 06/22 - 07/19
08. 07/20 - 08/16
09. 08/17 - 09/13
10. 09/14 - 10/11
11. 10/12 - 11/08
12. 11/09 - 12/06
13. 12/07 - 12/31
*) Make sure <book:slots> really works
=========================== end of file ========================

View File

@@ -1,10 +1,10 @@
Lots
====
Implementation Proposal
Architecture & Implementation Overview
Linas Vepstas <linas@linas.org>
Last Revised January 2003
Last Revised August 2003
One often needs to know that the item 'bought' in one transaction
@@ -170,12 +170,11 @@ its own.
Closing of Books
================
A few words on lots and the closing of books. Without implementing
lots in the GnuCash engine, there is really no way of correctly
implementing book closing. That is because some reports, such as
the capital-gains report, need to know not only the account balance,
but also the purchase dates. Lots provide the natural mechanism for
storing this information.
A few words on lots and the closing of books. Without lots, there
is really no way of correctly implementing book closing. That is
because some reports, such as the capital-gains report, need to know
not only the account balance, but also the purchase dates. Lots
provide the natural mechanism for storing this information.
When a book is closed, any open lots must be moved into/kept with
the open book. Since the splits in a lot belong to transactions,
@@ -185,10 +184,15 @@ is considered closed when its balance is zero; when a book is closed,
all of the lots that were closed stay with that book. That is,
closed lots are not propagated forward into the currently open book.
Actually, its slightly more subtle than that. Not only must open
lots stay with the open book, but so must all transactions that
have splits that participate in teh open lot, and, by extension,
all closed lots that participate in these 'open transations',
ad infinitum.
The "Double Balance" Proposal
=============================
The "Double Balance" Requirment
===============================
The following is a proposal for how to handle both realized
and unrealized gains/losses by means of "adjusting transactions."
It works for simple cases, but has issues for more complex cases.
@@ -330,16 +334,62 @@ we want to show only one of these two splits. Whichever method
is chosen, the register has to filter out some of the splits
that it shows.
The implementation that seems best is to represnet the sale
The implementation that seems best is to represent the sale
with two separate transactions: one for the sale itself, and a
separate one for the gains. This makes computing the balance
easier, although it makes the logic for setting the date
easier, although it makes the logic for setting the date
more complex. Ughh..
Cap Gains Implementation Notes
==============================
Cap Gains will be handled by GnuCash as described above, using
two distinct transactions. These transactions will be marked up
using KVP, pointing to each other, so that the one can be found
from the other. Implementation in src/engine/cap-gains.c
Quick API Overview:
xaccSplitGetCapGains(): Returns the capital gains associated with
a split. Split must have been a sale/purchase in a previously
opened lot.
xaccSplitAssignToLot(): If a split is not already in a lot,
then it places it into a lot, using a FIFO accounting policy.
TODO:
-- need to recompute lot membership when source split 'amount' changes.
-- need to recompute gain value when source split value changes.
-- need to copy void status when source split date changes.
Cap Gains Actual Implementation
===============================
Cap Gains are noted by creating a separate transaction with two
splits in it. One of the splits is as described above: zero
amount, non-zero value. There is a GUI requirement that when
the looking at a gains transaction, certain things need to be
kept in sync with the transaction that is the source of the gains.
In order to accomplish this, the engine uses a set of 'dirty'
flags, and a pair of pointers between the gains split and the
source split, so that the one can be quickly found from the other.
Things kept in sync:
-- date posted
-- value
-- void status
-- other things ?
Things not kept in sync:
-- kvp trees
-- description, memo, other things?
The posted date is kept in sync using a lazy-evaluation scheme.
If xaccTransactionSetDatePosted() is called, the date change is
accepted, and the split is marked date-dirty. If the posted date
is queried for (using GetDatePosted()), then the transaction is
evaluated. If its a gains-transaction, then it's date is copied
from the source transaction that created the gains.
Conversion
==========
When Lots are finally put into production, old GnuCash datasets
As Lots are put into production, old GnuCash datasets
will need to be converted. Conversion will be done by running
all splits in an account through an accounting FIFO. The goal
of the FIFO is to match up purchases and sales so that these can
@@ -361,6 +411,13 @@ for each account {
See the file Scrub2.h for details.
There is a bit of a problem with this conversion proceedure: If the
user had prviously recorded cap gains using a 'handmade' version of
lots, those cap gains will be ignored and will throw off balances.
User will need to hand-edit to recover.
-- TODO: Modify scrub routines to look for splits with zero amount,
try to assign these to lots as appropriate. ??
GUI
===
@@ -415,18 +472,27 @@ Status
o See src/engine/gnc-lot.h for the public API.
o The XML backend support for lot is complete (April 2002, ships in
version 1.8.x).
o The Postgres backend does not yet support lots.
o Scrub routines to automatically take old gnucash datasets and
transition them to double-balanced lots have been implemented.
These implement a FIFO accounting policy (April 2003)
o Closed/Open lots are handled correctly during book closing.
See src/engine/Period.c (August 2003)
XXX need to add a lot id, have it auto-gened by the scrubber.
o The Postgres backend does not yet support lots.
XXX In particular, the programming API for Lots is currently
non-transactional. There's no dirty flag, and the backend is not
signalled that the lots have changed. This is the case both for
Scrub2.c and for src/gnome/lot-viewer.c (which changes KVP's).
o No GUI yet.
o prototype lot-viewing GUI in src/gnome/lot-viewer.c
This GUI cannot 'edit' lots.
o Work has begun on the automatic computation & tracking of gain/loss
transactions. See src/engine/cap-gains.h Routines now exist to
compute these. Need to add infrastructure to auto-recompute as
needed, make use of this in register gui's, etc. A lot of work left.
In particular, what about the cap-gains report?
XXX It is a good idea to generate a gain/loss split for ever sale, not
just the lot closure. Need to tweak the register to hide/show as appropriate.
Need to add a tag to the gain split to indicate its's 'special' nature.
-------------------------- end of file ------------------------------

View File

@@ -1,7 +1,7 @@
/********************************************************************\
* Account.c -- Account data structure implementation *
* Copyright (C) 1997 Robin D. Clark *
* Copyright (C) 1997-2002 Linas Vepstas <linas@linas.org> *
* Copyright (C) 1997-2003 Linas Vepstas <linas@linas.org> *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
@@ -39,6 +39,7 @@
#include "gnc-lot.h"
#include "gnc-lot-p.h"
#include "gnc-pricedb.h"
#include "gnc-trace.h"
#include "kvp_frame.h"
#include "kvp-util-p.h"
#include "messages.h"
@@ -215,9 +216,13 @@ xaccCloneAccount (const Account *from, QofBook *book)
ret->non_standard_scu = from->non_standard_scu;
ret->core_dirty = TRUE;
/* make a note of where the copy came from */
gnc_kvp_gemini (ret->kvp_data, &from->guid, &from->book->guid, now);
gnc_kvp_gemini (from->kvp_data, &ret->guid, &book->guid, now);
/* Make a note of where the copy came from */
gnc_kvp_gemini (ret->kvp_data, now, "acct_guid", &from->guid,
"book_guid", &from->book->guid,
NULL);
gnc_kvp_gemini (from->kvp_data, now, "acct_guid", &ret->guid,
"book_guid", &book->guid,
NULL);
LEAVE (" ");
return ret;
@@ -228,44 +233,21 @@ xaccCloneAccount (const Account *from, QofBook *book)
Account *
xaccAccountLookupTwin (Account *acc, QofBook *book)
{
KvpValue *v_ncopies;
int i, ncopies = 0;
KvpFrame *fr;
GUID * twin_guid;
Account * twin;
if (!acc || !book) return NULL;
ENTER (" ");
v_ncopies = kvp_frame_get_slot_path (acc->kvp_data, "gemini", "ncopies", NULL);
if (!v_ncopies) return NULL;
ncopies = kvp_value_get_gint64 (v_ncopies);
for (i=0; i<ncopies; i++)
{
GUID * book_guid;
KvpValue *v_book_guid;
char buff[80];
fr = gnc_kvp_bag_find_by_guid (acc->kvp_data, "gemini",
"book_guid", &book->guid);
sprintf (buff, "%d", i);
v_book_guid = kvp_frame_get_slot_path (acc->kvp_data,
"gemini", buff, "book_guid", NULL);
if (!v_book_guid) continue;
book_guid = kvp_value_get_guid (v_book_guid);
twin_guid = kvp_frame_get_guid (fr, "acct_guid");
twin = xaccAccountLookup (twin_guid, book);
if (guid_equal(book_guid, &book->guid))
{
Account *twin;
GUID * acct_guid;
KvpValue *v_acct_guid;
v_acct_guid = kvp_frame_get_slot_path (acc->kvp_data,
"gemini", buff, "acct_guid", NULL);
if (!v_acct_guid) return NULL;
acct_guid = kvp_value_get_guid (v_acct_guid);
twin = xaccAccountLookup (acct_guid, book);
return twin;
}
}
LEAVE (" ");
return NULL;
LEAVE (" found twin=%p", twin);
return twin;
}
/********************************************************************\
@@ -414,6 +396,7 @@ xaccAccountCommitEdit (Account *acc)
acc->editlevel--;
if (0 < acc->editlevel) return;
ENTER (" ");
if (0 > acc->editlevel)
{
PERR ("unbalanced call - resetting (was %d)", acc->editlevel);
@@ -509,6 +492,7 @@ xaccAccountCommitEdit (Account *acc)
xaccGroupRemoveAccount(acc->parent, acc);
xaccFreeAccount(acc);
}
LEAVE (" ");
}
void
@@ -1032,6 +1016,7 @@ void
xaccAccountInsertSplit (Account *acc, Split *split)
{
Transaction *trans;
gnc_numeric old_amt;
if (!acc) return;
if (!split) return;
@@ -1041,6 +1026,7 @@ xaccAccountInsertSplit (Account *acc, Split *split)
g_return_if_fail (acc->book == split->book);
trans = xaccSplitGetParent (split);
old_amt = xaccSplitGetAmount (split);
xaccAccountBeginEdit(acc);
xaccTransBeginEdit(trans);
@@ -1048,20 +1034,14 @@ xaccAccountInsertSplit (Account *acc, Split *split)
acc->balance_dirty = TRUE;
acc->sort_dirty = TRUE;
/* convert the split to the new account's denominator */
/* if the denominator can't be exactly converted, it's an error */
/* FIXME : need to enforce ordering of insertion/value */
split->amount = gnc_numeric_convert(split->amount,
xaccAccountGetCommoditySCU(acc),
GNC_RND_ROUND);
/* if this split belongs to another account, remove it from there
* first. We don't want to ever leave the system in an inconsistent
* state. Note that it might belong to the current account if we're
* just using this call to re-order. */
if (xaccSplitGetAccount(split) &&
xaccSplitGetAccount(split) != acc)
xaccAccountRemoveSplit (xaccSplitGetAccount(split), split);
/* If this split belongs to another account, remove it from there
* first. We don't want to ever leave the system in an inconsistent
* state. Note that it might belong to the current account if we're
* just using this call to re-order. */
if (split->acc && split->acc != acc)
{
xaccAccountRemoveSplit (split->acc, split);
}
split->acc = acc;
if (split->lot && (NULL == split->lot->account))
@@ -1078,13 +1058,16 @@ xaccAccountInsertSplit (Account *acc, Split *split)
acc->sort_dirty = FALSE;
}
else
{
acc->splits = g_list_prepend(acc->splits, split);
}
mark_account (acc);
if (split->parent)
gnc_engine_generate_event (&split->parent->guid, GNC_ID_TRANS, GNC_EVENT_MODIFY);
}
/* Setting the amount casues a conversion to the new account's
* denominator AKA 'SCU Smallest Currency Unit'. */
xaccSplitSetAmount(split, old_amt);
xaccTransCommitEdit(trans);
xaccAccountCommitEdit(acc);
LEAVE ("(acc=%p, split=%p)", acc, split);
@@ -1098,6 +1081,8 @@ xaccAccountRemoveSplit (Account *acc, Split *split)
{
if (!acc) return;
if (!split) return;
if (split->acc && split->acc != acc) return;
ENTER ("(acc=%p, split=%p)", acc, split);
xaccAccountBeginEdit(acc);
@@ -1120,7 +1105,9 @@ xaccAccountRemoveSplit (Account *acc, Split *split)
xaccTransBeginEdit (trans);
split->acc = NULL;
if (split->lot)
/* Remove from lot (but only if it hasn't been moved to new lot already) */
if (split->lot && split->lot->account == acc)
{
gnc_lot_remove_split (split->lot, split);
}
@@ -1184,16 +1171,17 @@ xaccAccountRecomputeBalance (Account * acc)
for(lp = acc->splits; lp; lp = lp->next) {
Split *split = (Split *) lp->data;
gnc_numeric amt = xaccSplitGetAmount (split);
balance = gnc_numeric_add_fixed(balance, split->amount);
balance = gnc_numeric_add_fixed(balance, amt);
if (CREC == split->reconciled)
cleared_balance = gnc_numeric_add_fixed(cleared_balance, split->amount);
if (NREC != split->reconciled)
cleared_balance = gnc_numeric_add_fixed(cleared_balance, amt);
if (YREC == split->reconciled ||
FREC == split->reconciled) {
reconciled_balance =
gnc_numeric_add_fixed(reconciled_balance, split->amount);
gnc_numeric_add_fixed(reconciled_balance, amt);
}
split->balance = balance;
@@ -1278,7 +1266,7 @@ xaccTransFixSplitDateOrder (Transaction *trans)
for (node = trans->splits; node; node = node->next)
{
Split *s = node->data;
xaccAccountFixSplitDateOrder (xaccSplitGetAccount(s), s);
xaccAccountFixSplitDateOrder (s->acc, s);
}
gnc_engine_resume_events();
}
@@ -1446,7 +1434,7 @@ xaccAccountSetNotes (Account *acc, const char *str)
xaccAccountCommitEdit(acc);
}
/* FIXME : is this the right way to do this? */
/* FIXME : is this the right way to do this? Uhh, I think so ?? */
static void
update_split_commodity(Account * acc)
{
@@ -1461,11 +1449,11 @@ update_split_commodity(Account * acc)
{
Split *s = (Split *) lp->data;
Transaction *trans = xaccSplitGetParent (s);
gnc_numeric amt;
amt = xaccSplitGetAmount (s);
xaccTransBeginEdit (trans);
s->amount = gnc_numeric_convert(s->amount,
xaccAccountGetCommoditySCU(acc),
GNC_RND_ROUND);
xaccSplitSetAmount (s, amt);
xaccTransCommitEdit (trans);
}
@@ -1484,6 +1472,7 @@ xaccAccountSetCommodity (Account * acc, gnc_commodity * com)
{
acc->commodity = com;
acc->commodity_scu = gnc_commodity_get_fraction(com);
acc->non_standard_scu = FALSE;
update_split_commodity(acc);
acc->sort_dirty = TRUE;
@@ -1492,25 +1481,13 @@ xaccAccountSetCommodity (Account * acc, gnc_commodity * com)
mark_account (acc);
}
acc->core_dirty = TRUE;
xaccAccountCommitEdit(acc);
if (gnc_commodity_is_iso(com)) {
if (gnc_commodity_is_iso(com))
{
/* compatability hack - Gnucash 1.8 gets currency quotes when a
non-default currency is assigned to an account. */
gnc_commodity_set_quote_flag(com, TRUE);
}
}
void
xaccAccountSetCommoditySCU (Account *acc, int scu)
{
if (!acc) return;
xaccAccountBeginEdit(acc);
{
acc->commodity_scu = scu;
mark_account (acc);
}
acc->core_dirty = TRUE;
xaccAccountCommitEdit(acc);
}
@@ -1521,7 +1498,7 @@ xaccAccountSetCommoditySCU (Account *acc, int scu)
* to mismatched values in the past.
*/
void
xaccAccountSetCommoditySCUandFlag (Account *acc, int scu)
xaccAccountSetCommoditySCU (Account *acc, int scu)
{
if (!acc) return;
@@ -1603,69 +1580,6 @@ DxaccAccountSetCurrency (Account * acc, gnc_commodity * currency)
}
}
void
DxaccAccountSetSecurity (Account *acc, gnc_commodity * security)
{
const char *string;
gnc_commodity *commodity;
if ((!acc) || (!security)) return;
xaccAccountBeginEdit(acc);
string = gnc_commodity_get_unique_name (security);
kvp_frame_set_slot_nc(acc->kvp_data, "old-security",
kvp_value_new_string(string));
mark_account (acc);
acc->core_dirty = TRUE;
xaccAccountCommitEdit(acc);
commodity = DxaccAccountGetSecurity (acc);
if (!commodity)
{
gnc_commodity_table_insert (gnc_commodity_table_get_table (acc->book), security);
}
}
void
DxaccAccountSetCurrencySCU (Account * acc, int scu)
{
if (!acc) return;
xaccAccountBeginEdit(acc);
kvp_frame_set_slot_nc(acc->kvp_data, "old-currency-scu",
kvp_value_new_gint64(scu));
mark_account (acc);
acc->core_dirty = TRUE;
xaccAccountCommitEdit(acc);
}
int
DxaccAccountGetCurrencySCU (Account * acc)
{
KvpValue *v;
if (!acc) return 0;
v = kvp_frame_get_slot(acc->kvp_data, "old-currency-scu");
if (v) return kvp_value_get_gint64 (v);
return 0;
}
/********************************************************************\
\********************************************************************/
void
xaccAccountDeleteOldData (Account *account)
{
if (!account) return;
kvp_frame_set_slot_nc (account->kvp_data, "old-currency", NULL);
kvp_frame_set_slot_nc (account->kvp_data, "old-security", NULL);
kvp_frame_set_slot_nc (account->kvp_data, "old-currency-scu", NULL);
kvp_frame_set_slot_nc (account->kvp_data, "old-security-scu", NULL);
}
/********************************************************************\
\********************************************************************/
@@ -1823,26 +1737,6 @@ xaccAccountGetCommodity (Account *acc)
return (acc->commodity);
}
gnc_commodity *
DxaccAccountGetSecurity (Account *acc)
{
KvpValue *v;
const char *s;
gnc_commodity_table *table;
if (!acc) return NULL;
v = kvp_frame_get_slot(acc->kvp_data, "old-security");
if (!v) return NULL;
s = kvp_value_get_string (v);
if (!s) return NULL;
table = gnc_commodity_table_get_table (acc->book);
return gnc_commodity_table_lookup_unique (table, s);
}
gnc_numeric
xaccAccountGetBalance (Account *acc)
{
@@ -1906,7 +1800,7 @@ xaccAccountGetBalanceAsOfDate (Account *acc, time_t date)
* it doesn't exist yet and I'm uncertain of exactly how
* it would work at this time, since it differs from
* xaccAccountForEachTransaction by using gpointer return
* values rather than gbooleans.
* values rather than gints.
*/
GList *lp;
Timespec ts, trans_ts;
@@ -2273,17 +2167,10 @@ xaccAccountGetTaxUSCode (Account *account)
void
xaccAccountSetTaxUSCode (Account *account, const char *code)
{
KvpFrame *frame;
if (!account)
return;
if (!account) return;
xaccAccountBeginEdit (account);
frame = kvp_frame_get_frame (account->kvp_data, "tax-US", NULL);
kvp_frame_set_slot_nc (frame, "code",
code ? kvp_value_new_string (code) : NULL);
kvp_frame_set_str (account->kvp_data, "/tax-US/code", code);
mark_account (account);
account->core_dirty = TRUE;
@@ -2293,33 +2180,17 @@ xaccAccountSetTaxUSCode (Account *account, const char *code)
const char *
xaccAccountGetTaxUSPayerNameSource (Account *account)
{
KvpValue *value;
if (!account)
return FALSE;
value = kvp_frame_get_slot_path (account->kvp_data,
"tax-US", "payer-name-source", NULL);
if (!value)
return NULL;
return kvp_value_get_string (value);
if (!account) return NULL;
return kvp_frame_get_string (account->kvp_data, "/tax-US/payer-name-source");
}
void
xaccAccountSetTaxUSPayerNameSource (Account *account, const char *source)
{
KvpFrame *frame;
if (!account)
return;
if (!account) return;
xaccAccountBeginEdit (account);
frame = kvp_frame_get_frame (account->kvp_data, "tax-US", NULL);
kvp_frame_set_slot_nc (frame, "payer-name-source",
source ? kvp_value_new_string (source) : NULL);
kvp_frame_set_str (account->kvp_data, "/tax-US/payer-name-source", source);
mark_account (account);
account->core_dirty = TRUE;
@@ -2622,14 +2493,11 @@ xaccAccountGetReconcileLastDate (Account *account, time_t *last_date)
void
xaccAccountSetReconcileLastDate (Account *account, time_t last_date)
{
KvpFrame *frame;
if (!account)
return;
if (!account) return;
xaccAccountBeginEdit (account);
frame = kvp_frame_get_frame (account->kvp_data, "reconcile-info", NULL);
kvp_frame_set_slot_nc (frame, "last-date",
kvp_value_new_gint64 (last_date));
kvp_frame_set_gint64 (account->kvp_data,
"/reconcile-info/last-date", last_date);
mark_account (account);
account->core_dirty = TRUE;
@@ -2669,16 +2537,15 @@ void
xaccAccountSetReconcileLastInterval (Account *account, int months, int days)
{
KvpFrame *frame;
if (!account)
return;
if (!account) return;
xaccAccountBeginEdit (account);
frame = kvp_frame_get_frame (account->kvp_data, "reconcile-info",
"last-interval", NULL);
kvp_frame_set_slot_nc (frame, "months",
kvp_value_new_gint64 (months));
kvp_frame_set_slot_nc (frame, "days",
kvp_value_new_gint64 (days));
frame = kvp_frame_get_frame (account->kvp_data,
"/reconcile-info/last-interval");
kvp_frame_set_gint64 (frame, "months", months);
kvp_frame_set_gint64 (frame, "days", days);
mark_account (account);
account->core_dirty = TRUE;
@@ -2720,16 +2587,13 @@ void
xaccAccountSetReconcilePostponeDate (Account *account,
time_t postpone_date)
{
KvpFrame *frame;
if (!account)
return;
if (!account) return;
xaccAccountBeginEdit (account);
frame = kvp_frame_get_frame (account->kvp_data,
"reconcile-info", "postpone", NULL);
kvp_frame_set_slot_nc (frame, "date",
kvp_value_new_gint64 (postpone_date));
/* XXX this should be using timespecs, not gints !! */
kvp_frame_set_gint64 (account->kvp_data,
"/reconcile-info/postpone/date", postpone_date);
mark_account (account);
account->core_dirty = TRUE;
@@ -2772,16 +2636,11 @@ void
xaccAccountSetReconcilePostponeBalance (Account *account,
gnc_numeric balance)
{
KvpFrame *frame;
if (!account)
return;
if (!account) return;
xaccAccountBeginEdit (account);
frame = kvp_frame_get_frame (account->kvp_data,
"reconcile-info", "postpone", NULL);
kvp_frame_set_slot_nc (frame, "balance",
kvp_value_new_gnc_numeric (balance));
kvp_frame_set_gnc_numeric (account->kvp_data,
"/reconcile-info/postpone/balance", balance);
mark_account (account);
account->core_dirty = TRUE;
@@ -2846,18 +2705,15 @@ xaccAccountGetAutoInterestXfer (Account *account, gboolean default_value)
void
xaccAccountSetAutoInterestXfer (Account *account, gboolean option)
{
KvpFrame *frame;
if (!account)
return;
xaccAccountBeginEdit (account);
frame = kvp_frame_get_frame (account->kvp_data,
"reconcile-info", NULL);
/* FIXME: need KVP_TYPE_BOOLEAN for this someday */
kvp_frame_set_slot_nc (frame, "auto-interest-transfer",
kvp_value_new_string (option ? "true" : "false"));
kvp_frame_set_str (account->kvp_data,
"/reconcile-info/auto-interest-transfer",
(option ? "true" : "false"));
mark_account (account);
account->core_dirty = TRUE;
@@ -2987,19 +2843,16 @@ dxaccAccountGetQuoteTZ(Account *acc)
void
xaccAccountSetReconcileChildrenStatus(Account *account, gboolean status)
{
KvpFrame *frame;
if (!account)
return;
if (!account) return;
xaccAccountBeginEdit (account);
frame = kvp_frame_get_frame (account->kvp_data, "reconcile-info", NULL);
kvp_frame_set_slot_nc (frame,
"include-children",
status ? kvp_value_new_gint64 (status) : NULL);
/* XXX FIXME: someday this should use KVP_TYPE_BOOLEAN */
kvp_frame_set_gint64 (account->kvp_data,
"/reconcile-info/include-children", status);
account->core_dirty = TRUE;
xaccAccountCommitEdit (account);
return;
}
/********************************************************************\
@@ -3027,60 +2880,14 @@ xaccAccountGetReconcileChildrenStatus(Account *account)
/********************************************************************\
\********************************************************************/
gboolean
xaccAccountVisitUnvisitedTransactions(Account *acc,
gboolean (*proc)(Transaction *t,
void *data),
void *data,
GHashTable *visited_txns)
{
gboolean keep_going = TRUE;
GList *lp;
if(!acc) return(FALSE);
if(!proc) return(FALSE);
if(!visited_txns) return(FALSE);
for(lp = acc->splits; lp && keep_going; lp = lp->next) {
Split *s = (Split *) lp->data;
Transaction *t = xaccSplitGetParent(s);
if(t) {
const GUID *guid = xaccTransGetGUID(t);
gpointer been_here = g_hash_table_lookup(visited_txns, guid);
if(!GPOINTER_TO_INT(been_here)) {
g_hash_table_insert(visited_txns, (gpointer) guid,
GINT_TO_POINTER(TRUE));
if(!proc(t, data)) {
keep_going = FALSE;
}
}
}
}
return(keep_going);
}
gboolean
gint
xaccAccountForEachTransaction(Account *acc,
gboolean (*proc)(Transaction *t, void *data),
TransactionCallback proc,
void *data)
{
GHashTable *visited_txns = NULL;
gboolean result = FALSE;
if(!acc) return(FALSE);
if(!proc) return(FALSE);
visited_txns = guid_hash_table_new();
if(visited_txns) {
result =
xaccAccountVisitUnvisitedTransactions(acc, proc, data, visited_txns);
}
/* cleanup */
if(visited_txns) g_hash_table_destroy(visited_txns);
return(result);
if(!acc || !proc) return 0;
xaccAccountBeginStagedTransactionTraversals (acc);
return xaccAccountStagedTransactionTraversal(acc, 42, proc, data);
}
/********************************************************************\

View File

@@ -260,53 +260,71 @@ void xaccAccountSetAutoInterestXfer (Account *account, gboolean value);
/** @} */
/* @name Account Commodity setters/getters
* Accounts are used to store an amount of 'something', that 'something'
* is called the 'commodity'. An account can only hold one kind of
* commodity. The following are used to get and set the commodity,
* and also to set the SCU, the 'Smallest Commodity Unit'.
*
* New commodity access routines.
* Note that when we say that a 'split' holds an 'amount', that amount
* is denominated in the account commodity. Do not confuse 'amount'
* and 'value'. The 'value' of a split is the value of the amount
* expressed in the currency fo the transaction. Thus, for example,
* the 'amount' may be 12 apples, where the account commodity is
* 'apples'. The value of these 12 apples may be 12 dollars, where
* the transaction currency is 'dollars'.
*
* The account structure no longer stores two commodities ('currency'
* and 'security'). Instead it stores only one commodity. This single
* commodity is the one formerly known as 'security'. Use
* xaccAccountSetCommodity() and xaccAccountGetCommodity() to set and
* fetch it. (This transition has been done between version 1.6.x and
* 1.7.0.)
* The SCU is the 'Smallest Commodity Unit', signifying the smallest
* non-zero amount that can be stored in the account. It is
* represented as the integer denominator of a fraction. Thus,
* for example, a SCU of 12 means that 1/12 of something is the
* smallest amount that can be stored in the account. SCU's can
* be any value; they do not need to be decimal. This allows
* the use of accounts with unusual, non-decimal commodities and
* currencies.
*
* Basically, the engine eliminates the 'currency' field of the
* Account structure. Instead, the common currency is stored with the
* transaction. The 'value' of a split is a translation of the
* Split's 'amount' (which is the amount of the Account's commodity
* involved) into the Transaction's balancing currency. */
* Normally, the SCU is determined by the commodity of the account.
* However, this default SCU can be over-ridden and set to an
* account-specific value. This is account-specific value is
* called the 'non-standard' value in the documentation below.
*/
/** @{ */
/** Set the account's commodity */
void xaccAccountSetCommodity (Account *account, gnc_commodity *comm);
/** Get the account's commodity
*
* This is from the new commodity access routines.
*
* The account structure no longer stores two commodities ('currency'
* and 'security'). Instead it stores only one commodity. This single
* commodity is the one formerly known as 'security'. Use
* xaccAccountSetCommodity() and xaccAccountGetCommodity() to set and
* fetch it. (This transition has been done between version 1.6.x and
* 1.7.0.)
*
* Basically, the engine eliminates the 'currency' field of the
* Account structure. Instead, the common currency is stored with the
* transaction. The 'value' of a split is a translation of the
* Split's 'amount' (which is the amount of the Account's commodity
* involved) into the Transaction's balancing currency. */
/* deprecated do not use */
#define DxaccAccountSetSecurity xaccAccountSetCommodity
/** Get the account's commodity */
gnc_commodity * xaccAccountGetCommodity (Account *account);
/** DOCUMENT ME! */
/* deprecated do not use */
#define DxaccAccountGetSecurity xaccAccountGetCommodity
/** Return the SCU for the account. If a non-standard SCU has been
* set for the account, that s returned; else the default SCU for
* the account commodity is returned.
*/
int xaccAccountGetCommoditySCU (Account *account);
/** DOCUMENT ME! */
/** Return the 'internal' SCU setting. This returns the over-ride
* SCU for the account (which might not be set, and might be zero). */
int xaccAccountGetCommoditySCUi (Account *account);
/** DOCUMENT ME! */
/** Set the SCU for the account. Normally, this routine is not
* required, as the default SCU for an account is given by its
* commodity.
*/
void xaccAccountSetCommoditySCU (Account *account, int frac);
/** DOCUMENT ME! */
void xaccAccountSetCommoditySCUandFlag (Account *account, int frac);
/** DOCUMENT ME! */
/* deprecated -- do not use for future development */
#define xaccAccountSetCommoditySCUandFlag xaccAccountSetCommoditySCU
/** Set the flag indicating that this account uses a non-standard SCU. */
void xaccAccountSetNonStdSCU (Account *account, gboolean flag);
/** DOCUMENT ME! */
/** Return boolean, indicating whether this account uses a
* non-standard SCU. */
gboolean xaccAccountGetNonStdSCU (Account *account);
/**@}*/
@@ -406,10 +424,6 @@ gboolean xaccAccountHasAncestor (Account *account, Account *ancestor);
/** @{ */
KvpFrame * xaccAccountGetSlots (Account *account);
void xaccAccountSetSlots_nc(Account *account, KvpFrame *frame);
/** Delete any old data in the account's kvp data.
* This includes the old currency and security fields. */
void xaccAccountDeleteOldData (Account *account);
/** @} */
/* ------------------ */
@@ -453,14 +467,6 @@ gboolean xaccAccountTypesCompatible (GNCAccountType parent_type,
/* ------------------ */
/* Doxygen note: if these typedefs are inside the member group, the
* member group will show up at the top of the documentation, which is
* probably not wanted. */
/** \warning Unimplemented, for xaccAccountForEachSplit() */
typedef gpointer (*SplitCallback)(Split *s, gpointer data);
/** Callback prototype for xaccAccountForEachTransaction() */
typedef gboolean (*TransactionCallback)(Transaction *t, void *data);
/** @name Account split/transaction list management */
/*@{*/
/** The xaccAccountInsertSplit() method will insert the indicated
@@ -486,7 +492,7 @@ gpointer xaccAccountForEachSplit(Account *account,
/** The xaccAccountForEachTransaction() routine will traverse all of
the transactions in the given 'account' and call the callback
function 'proc' on each transaction. Processing will continue
if-and-only-if 'proc' does not return FALSE. The user data pointer
if-and-only-if 'proc' returns 0. The user data pointer
'data' will be passed on to the callback function 'proc'.
This function does not descend recursively to traverse transactions
@@ -495,12 +501,9 @@ gpointer xaccAccountForEachSplit(Account *account,
'proc' will be called exactly once for each transaction that is
pointed to by at least one split in the given account.
Note too, that if you call this function on two separate accounts
and those accounts share transactions, proc will be called once per
account for the shared transactions.
The result of this function will not be FALSE if-and-only-if
The result of this function will be 0 if-and-only-if
every relevant transaction was traversed exactly once.
Else the return value is the last non-zero value returned by proc.
Note that the traversal occurs only over the transactions that
are locally cached in the local gnucash engine. If the gnucash
@@ -510,34 +513,16 @@ gpointer xaccAccountForEachSplit(Account *account,
it will not traverse transactions present only in the remote
database.
*/
gboolean
xaccAccountForEachTransaction(Account *account,
TransactionCallback proc,
void *data);
/** The xaccAccountVisitUnvisitedTransactions() routine will
visit every transaction in the account that hasn't already been
visited exactly once. visited_txns must be a hash table created
via guid_hash_table_new() and is the authority about which
transactions have already been visited. Further, when this
procedure returns visited_txns will have been modified to reflect
all the newly visited transactions.
The result of this function will not be FALSE if-and-only-if
every relevant transaction was traversed exactly once.
*/
gboolean
xaccAccountVisitUnvisitedTransactions(Account *account,
TransactionCallback,
void *data,
GHashTable *visited_txns);
gint xaccAccountForEachTransaction(Account *account,
TransactionCallback proc,
void *data);
/** Returns a pointer to the transaction, not a copy. */
Transaction *
xaccAccountFindTransByDesc(Account *account, const char *description);
Transaction * xaccAccountFindTransByDesc(Account *account,
const char *description);
/** Returns a pointer to the split, not a copy. */
Split *
xaccAccountFindSplitByDesc(Account *account, const char *description);
Split * xaccAccountFindSplitByDesc(Account *account, const char *description);
/** The xaccAccountFixSplitDateOrder() subroutine checks to see if
* a split is in proper sorted date order with respect
@@ -563,7 +548,12 @@ void xaccAccountInsertLot (Account *, GNCLot *);
void xaccAccountRemoveLot (Account *, GNCLot *);
/** The xaccAccountGetLotList() routine returns a pointer to the GList of
* the lots in this account. The same warnings as above apply. */
* the lots in this account.
* @note This GList is the account's internal
* data structure: do not delete it when done; treat it as a read-only
* structure. Note that some routines (such as xaccAccountRemoveLot())
* modify this list directly, and could leave you with a corrupted
* pointer. */
LotList* xaccAccountGetLotList (Account *account);
/** The xaccAccountForEachLot() method will apply the function 'proc'
@@ -703,38 +693,18 @@ void xaccClearMarkDownGr (AccountGroup *group, short val);
* These two funcs take control of their gnc_commodity args. Don't free */
void DxaccAccountSetCurrency (Account *account, gnc_commodity *currency);
/** @deprecated The current API associates only one thing with an
* account: the 'commodity'. Use xaccAccountGetCommodity() to fetch
* it.
*
* These two funcs take control of their gnc_commodity args. Don't free */
void DxaccAccountSetSecurity (Account *account, gnc_commodity *security);
/** @deprecated The current API associates only one thing with an
* account: the 'commodity'. Use xaccAccountGetCommodity() to fetch
* it. */
gnc_commodity * DxaccAccountGetCurrency (Account *account);
/** @deprecated The current API associates only one thing with an
* account: the 'commodity'. Use xaccAccountGetCommodity() to fetch
* it. */
gnc_commodity * DxaccAccountGetSecurity (Account *account);
/** @deprecated The current API associates only one thing with an
* account: the 'commodity'. Use xaccAccountGetCommodity() to fetch
* it. */
void DxaccAccountSetCurrencySCU (Account *account, int frac);
/** @deprecated The current API associates only one thing with an
* account: the 'commodity'. Use xaccAccountGetCommodity() to fetch
* it. */
int DxaccAccountGetCurrencySCU (Account *account);
/** Set the timezone to be used when interpreting the results from a
* given Finance::Quote backend. Unfortunately, the upstream sources
* don't label their output, so the user has to specify this bit.
*
* @deprecated Price quote information is now stored on the
* commodity, not the account. */
void dxaccAccountSetQuoteTZ (Account *account, const char *tz);
/** Get the timezone to be used when interpreting the results from a
* given Finance::Quote backend. Unfortunately, the upstream sources

View File

@@ -89,6 +89,7 @@
#include "gnc-engine-util.h"
#include "gnc-event-p.h"
#include "messages.h"
#include "gnc-trace.h"
#include "qofbook.h"
#include "qofbook-p.h"
#include "qofid-p.h"

View File

@@ -35,6 +35,7 @@
#include "gnc-engine-util.h"
#include "gnc-event-p.h"
#include "gnc-numeric.h"
#include "gnc-trace.h"
#include "qofbackend.h"
#include "qofbook.h"
#include "qofbook-p.h"
@@ -1136,90 +1137,15 @@ xaccGroupStagedTransactionTraversal (AccountGroup *grp,
/********************************************************************\
\********************************************************************/
struct group_visit_data
{
gboolean (*proc)(Transaction *t, void *data);
void *up_data;
GHashTable *visit_table;
};
static gboolean
xaccGroupVisitUnvisitedTransactions_thunk(Transaction *trn,
void *data)
{
gpointer test_trn;
struct group_visit_data *grdata = (struct group_visit_data*)data;
gboolean result;
test_trn = g_hash_table_lookup(grdata->visit_table, trn);
if(!test_trn)
{
g_hash_table_insert(grdata->visit_table, trn, "");
result = grdata->proc(trn, grdata->up_data);
}
else
result = TRUE;
return result;
}
gboolean
xaccGroupVisitUnvisitedTransactions (AccountGroup *g,
gboolean (*proc)(Transaction *t,
void *data),
void *data,
GHashTable *visited_txns)
{
gboolean keep_going = TRUE;
GList *list;
GList *node;
struct group_visit_data grdata;
if (!g) return(FALSE);
if (!proc) return(FALSE);
if (!visited_txns) return(FALSE);
list = xaccGroupGetSubAccounts (g);
grdata.proc = proc;
grdata.up_data = data;
grdata.visit_table = visited_txns;
for (node = list; node && keep_going; node = node->next)
{
Account *account = node->data;
keep_going = xaccAccountForEachTransaction(
account, xaccGroupVisitUnvisitedTransactions_thunk, (void*)&grdata);
}
g_list_free (list);
return(keep_going);
}
gboolean
int
xaccGroupForEachTransaction (AccountGroup *g,
gboolean (*proc)(Transaction *t, void *data),
int (*proc)(Transaction *t, void *data),
void *data)
{
GHashTable *visited_txns = NULL;
gboolean result = FALSE;
if (!g || !proc) return 0;
if (!g) return(FALSE);
if (!proc) return(FALSE);
visited_txns = guid_hash_table_new();
if (visited_txns)
result = xaccGroupVisitUnvisitedTransactions(g, proc, data, visited_txns);
/* cleanup */
if (visited_txns)
g_hash_table_destroy(visited_txns);
return(result);
xaccGroupBeginStagedTransactionTraversals (g);
return xaccGroupStagedTransactionTraversal (g, 42, proc, data);
}
/********************************************************************\

View File

@@ -300,10 +300,9 @@ gboolean xaccSplitTransactionTraverse(Split *split, int stage);
* a traversal is undefined, so don't do that.
*/
typedef int (*TransactionCallbackInt)(Transaction *t, void *data);
int xaccGroupStagedTransactionTraversal(AccountGroup *grp,
unsigned int stage,
TransactionCallbackInt,
TransactionCallback,
void *data);
/* xaccAccountStagedTransactionTraversal() calls thunk on each
@@ -320,11 +319,11 @@ int xaccGroupStagedTransactionTraversal(AccountGroup *grp,
int xaccAccountStagedTransactionTraversal(Account *a,
unsigned int stage,
TransactionCallbackInt,
TransactionCallback,
void *data);
/** Traverse all of the transactions in the given account group.
Continue processing IFF proc does not return FALSE. This function
Continue processing IFF proc returns 0. This function
will descend recursively to traverse transactions in the
children of the accounts in the group.
@@ -332,12 +331,9 @@ int xaccAccountStagedTransactionTraversal(Account *a,
pointed to by at least one split in any account in the hierarchy
topped by AccountGroup g.
Note too, that if you call this function on two separate account
groups and those accounts groups share transactions, proc will be
called once per account on the shared transactions.
The result of this function will not be FALSE IFF every relevant
transaction was traversed exactly once.
The result of this function will be 0 IFF every relevant
transaction was traversed exactly once; otherwise, the return
value is the last non-zero value returned by the callback.
Note that the traversal occurs only over the transactions that
are locally cached in the local gnucash engine. If the gnucash
@@ -346,26 +342,14 @@ int xaccAccountStagedTransactionTraversal(Account *a,
This routine will not cause an SQL database query to be performed;
it will not traverse transactions present only in the remote
database.
Note that this routine is just a trivial wrapper for
xaccGroupBeginStagedTransactionTraversals(grp);
xaccGroupStagedTransactionTraversal(grp, 42, cb, data);
*/
gboolean
xaccGroupForEachTransaction(AccountGroup *g,
TransactionCallback,
void *data);
/* Visit every transaction in the account that hasn't already been
visited exactly once. visited_txns must be a hash table created
via guid_hash_table_new() and is the authority about which
transactions have already been visited. Further, when this
procedure returns, visited_txns will have been modified to reflect
all the newly visited transactions.
The result of this function will not be FALSE IFF every relevant
transaction was traversed exactly once. */
gboolean
xaccGroupVisitUnvisitedTransactions(AccountGroup *g,
TransactionCallback,
void *data,
GHashTable *visited_txns);
int xaccGroupForEachTransaction(AccountGroup *g,
TransactionCallback, void *data);
#endif /* XACC_ACCOUNT_GROUP_H */

View File

@@ -1,4 +1,5 @@
SUBDIRS = . test-core test
PWD := $(shell pwd)
pkglib_LTLIBRARIES = libgw-engine.la libgw-kvp.la libgncmod-engine.la
@@ -19,8 +20,10 @@ libgncmod_engine_la_SOURCES = \
SX-ttinfo.c \
Scrub.c \
Scrub2.c \
Scrub3.c \
TransLog.c \
Transaction.c \
cap-gains.c \
gnc-associate-account.c \
gnc-commodity.c \
gnc-date.c \
@@ -38,6 +41,7 @@ libgncmod_engine_la_SOURCES = \
kvp-util.c \
md5.c \
messages.c \
policy.c \
qofbackend.c \
qofbook.c \
qofid.c \
@@ -65,8 +69,10 @@ gncinclude_HEADERS = \
QueryCore.h \
Scrub.h \
Scrub2.h \
Scrub3.h \
TransLog.h \
Transaction.h \
cap-gains.h \
engine-helpers.h \
glib-helpers.h \
gnc-associate-account.h \
@@ -88,6 +94,7 @@ gncinclude_HEADERS = \
kvp-scm.h \
kvp-util.h \
messages.h \
policy.h \
qof.h \
qofbackend.h \
qofbook.h \
@@ -116,6 +123,7 @@ noinst_HEADERS = \
md5.h \
gw-engine.h \
gw-kvp.h \
policy-p.h \
qofbackend-p.h \
qofbook-p.h \
qofid-p.h \
@@ -146,7 +154,6 @@ gncmod_DATA = engine.scm
gncscmdir = ${GNC_SHAREDIR}/scm
gncscm_DATA = \
commodity-table.scm \
engine-init.scm \
engine-interface.scm \
engine-utilities.scm \
gnc-numeric.scm

View File

@@ -23,21 +23,24 @@
* Period.c
*
* FUNCTION:
* Implement accounting periods.
* Implement accounting periods, using design described in
* src/doc/books.txt
*
* CAUTION: this is currently a semi-functional, untested implementation
* of the design described in src/doc/book.txt
Open questions: how do we deal with the backends ???
* CAUTION: poorly tested.
*
* HISTORY:
* created by Linas Vepstas November 2001
* Copyright (c) 2001, 2002 Linas Vepstas <linas@linas.org>
* Created by Linas Vepstas November 2001
* Copyright (c) 2001-2003 Linas Vepstas <linas@linas.org>
*/
#include "AccountP.h"
#include "gnc-engine-util.h"
#include "gnc-event-p.h"
#include "gnc-lot.h"
#include "gnc-lot-p.h"
#include "gnc-pricedb.h"
#include "gnc-pricedb-p.h"
#include "gnc-trace.h"
#include "Group.h"
#include "GroupP.h"
#include "kvp-util-p.h"
@@ -66,9 +69,10 @@ gnc_book_insert_trans_clobber (QofBook *book, Transaction *trans)
if (!trans || !book) return;
/* if this is the same book, its a no-op. */
/* If this is the same book, its a no-op. */
if (trans->book == book) return;
ENTER ("trans=%p %s", trans, trans->description);
newtrans = xaccDupeTransaction (trans);
for (node = newtrans->splits; node; node = node->next)
{
@@ -103,8 +107,6 @@ gnc_book_insert_trans_clobber (QofBook *book, Transaction *trans)
}
else
{
/* force to null, so remove doesn't occur */
s->acc = NULL;
xaccAccountInsertSplit (twin, s);
twin->balance_dirty = TRUE;
twin->sort_dirty = TRUE;
@@ -113,10 +115,11 @@ gnc_book_insert_trans_clobber (QofBook *book, Transaction *trans)
xaccTransCommitEdit (newtrans);
gnc_engine_generate_event (&newtrans->guid, GNC_ID_TRANS, GNC_EVENT_CREATE);
LEAVE ("trans=%p %s", trans, trans->description);
}
/* ================================================================ */
/* Reparent transaction to new book. This routine does this by simply
/* Reparent transaction to new book. This routine does this by
* moving GUID's to the new book's entity tables.
*/
@@ -137,6 +140,7 @@ gnc_book_insert_trans (QofBook *book, Transaction *trans)
gnc_book_insert_trans_clobber (book, trans);
return;
}
ENTER ("trans=%p %s", trans, trans->description);
/* Fiddle the transaction into place in the new book */
xaccTransBeginEdit (trans);
@@ -150,12 +154,15 @@ gnc_book_insert_trans (QofBook *book, Transaction *trans)
Account *twin;
Split *s = node->data;
/* move the split into the new book ... */
qof_entity_remove (s->book->entity_table, &s->guid);
s->book = book;
qof_entity_store(book->entity_table, s, &s->guid, GNC_ID_SPLIT);
/* Move the splits over (only if they haven't already been moved). */
if (s->book != book)
{
qof_entity_remove (s->book->entity_table, &s->guid);
s->book = book;
qof_entity_store(book->entity_table, s, &s->guid, GNC_ID_SPLIT);
}
/* find the twin account, and re-parent to that. */
/* Find the twin account, and re-parent to that. */
twin = xaccAccountLookupTwin (s->acc, book);
if (!twin)
{
@@ -163,37 +170,363 @@ gnc_book_insert_trans (QofBook *book, Transaction *trans)
}
else
{
/* force to null, so remove doesn't occur */
s->acc = NULL;
xaccAccountInsertSplit (twin, s);
twin->balance_dirty = TRUE;
twin->sort_dirty = TRUE;
/* Move the split too, if it hasn't been moved already */
if (s->acc != twin)
{
xaccAccountInsertSplit (twin, s);
twin->balance_dirty = TRUE;
twin->sort_dirty = TRUE;
}
}
}
xaccTransCommitEdit (trans);
gnc_engine_generate_event (&trans->guid, GNC_ID_TRANS, GNC_EVENT_MODIFY);
LEAVE ("trans=%p %s", trans, trans->description);
}
/* ================================================================ */
/* Reparent lot to new book. This routine does this by
* completely deleting and recreating the lot.
*/
void
gnc_book_insert_lot_clobber (QofBook *book, GNCLot *lot)
{
PERR ("Not Implemented");
}
/* ================================================================ */
/* Reparent lot to new book. This routine does this by
* moving GUID's to the new book's entity tables.
*/
void
gnc_book_insert_lot (QofBook *book, GNCLot *lot)
{
SplitList *snode;
Account *twin;
if (!lot || !book) return;
/* If this is the same book, its a no-op. */
if (lot->book == book) return;
if (book->backend != lot->book->backend)
{
gnc_book_insert_lot_clobber (book, lot);
return;
}
ENTER ("lot=%p", lot);
qof_entity_remove (lot->book->entity_table, &lot->guid);
lot->book = book;
qof_entity_store(book->entity_table, lot, &lot->guid, GNC_ID_LOT);
/* Move the splits over (only if they haven't already been moved). */
for (snode = lot->splits; snode; snode=snode->next)
{
Split *s = snode->data;
if (s->book != book)
{
qof_entity_remove (s->book->entity_table, &s->guid);
s->book = book;
qof_entity_store(book->entity_table, s, &s->guid, GNC_ID_SPLIT);
}
}
twin = xaccAccountLookupTwin (lot->account, book);
if (!twin)
{
PERR ("near-fatal: twin account not found");
}
else
{
xaccAccountInsertLot (twin, lot);
}
LEAVE ("lot=%p", lot);
}
/* ================================================================ */
void
gnc_book_partition (QofBook *dest_book, QofBook *src_book, Query *query)
gnc_book_insert_price (QofBook *book, GNCPrice *pr)
{
AccountGroup *src_grp, *dst_grp;
QofBackend *be;
time_t now;
GList *split_list, *snode;
if (!pr || !book) return;
/* If this is the same book, its a no-op. */
if (pr->book == book) return;
/* If the old and new book don't share backends, then clobber-copy;
* i.e. destroy it in one backend, create it in another. */
if (book->backend != pr->book->backend)
{
gnc_book_insert_price_clobber (book, pr);
return;
}
ENTER ("price=%p", pr);
/* Fiddle the price into place in the new book */
gnc_price_ref (pr);
gnc_price_begin_edit (pr);
qof_entity_remove (pr->book->entity_table, &pr->guid);
pr->book = book;
qof_entity_store(book->entity_table, pr, &pr->guid, GNC_ID_PRICE);
gnc_pricedb_remove_price (pr->db, pr);
gnc_pricedb_add_price (gnc_pricedb_get_db (book), pr);
gnc_price_commit_edit (pr);
gnc_price_unref (pr);
LEAVE ("price=%p", pr);
}
/* ================================================================ */
void
gnc_book_insert_price_clobber (QofBook *book, GNCPrice *pr)
{
PERR ("Not Implemented");
}
/* ================================================================ */
/* The following routines determine whether a given lot or
* transaction is linked or related to another lot that is 'open'.
* These return true if so.
*
* An 'open transaction' is a transaction that has a split
* that belongs to an 'open lot'. An 'open lot' is one that
* is not closed, OR ONE THAT HAS a split in it that belongs to
* an open transaction.
*
* The need for this recursive definition is that some lots,
* even though themselves closed, are participants in transactions
* that cannot be moved to a closed book, and thus, by association
* can't be moved either.
*
* Lots contain pointers to splits, and transactions contain
* pointers to splits. Together, these form a graph, which may
* be cyclic. We want to walk the entire graph, and determine
* whether there are any open lots in it. The walk must be
* recursive, and because it might be cyclic, we use a marker
* to break the cycles.
*/
static gboolean trans_has_open_lot_tree (Transaction *trans);
static gboolean lot_has_open_trans_tree (GNCLot *lot);
static gboolean
trans_has_open_lot_tree (Transaction *trans)
{
SplitList *split_list, *node;
if (1 == trans->marker) return FALSE;
if (2 == trans->marker) return TRUE;
trans->marker = 1;
split_list = xaccTransGetSplitList (trans);
for (node = split_list; node; node=node->next)
{
Split *s = node->data;
GNCLot *lot = s->lot;
if (NULL == lot) continue;
if ((FALSE == gnc_lot_is_closed(lot)) ||
(lot_has_open_trans_tree (lot)))
{
trans->marker = 2;
return TRUE;
}
}
return FALSE;
}
static gboolean
lot_has_open_trans_tree (GNCLot *lot)
{
SplitList *split_list, *snode;
if (1 == lot->marker) return FALSE;
if (2 == lot->marker) return TRUE;
lot->marker = 1;
if (FALSE == gnc_lot_is_closed(lot))
{
lot->marker = 2;
return TRUE;
}
split_list = gnc_lot_get_split_list (lot);
for (snode = split_list; snode; snode=snode->next)
{
Split *s = snode->data;
Transaction *trans = s->parent;
if (trans_has_open_lot_tree (trans))
{
lot->marker = 2;
return TRUE;
}
}
return FALSE;
}
/* ================================================================ */
/* The following routines remove 'open lots' and 'open transactions'
* from the lists passed in.
*/
static LotList *
lot_list_preen_open_lots (LotList *lot_list)
{
LotList *lnode;
ENTER (" ");
for (lnode=lot_list; lnode; )
{
GNCLot *lot = lnode->data;
LotList *lnext = lnode->next;
if (lot_has_open_trans_tree (lot))
{
lot_list = g_list_remove_link (lot_list, lnode);
/* XXX freeing this node somehow leads to glib g_list
* memory corruption which later takes down the system.
* I don't see why. */
/* g_list_free_1 (lnode); */
}
lnode = lnext;
}
LEAVE (" ");
return lot_list;
}
static TransList *
trans_list_preen_open_lots (TransList *trans_list)
{
TransList *tnode;
ENTER (" ");
for (tnode=trans_list; tnode; )
{
Transaction *trans = tnode->data;
TransList *tnext = tnode->next;
if (trans_has_open_lot_tree (trans))
{
trans_list = g_list_remove_link (trans_list, tnode);
/* XXX freeing this node somehow leads to glib g_list
* memory corruption which later takes down the system.
* I don't see why. */
/* g_list_free_1 (tnode); */
}
tnode = tnext;
}
LEAVE (" ");
return trans_list;
}
/* ================================================================ */
/* clear the markers for the above routines */
static void
clear_markers (AccountGroup *grp)
{
GList *node;
if (!grp) return;
for (node = grp->accounts; node; node = node->next)
{
Account *account = node->data;
GList *lp;
/* recursively do sub-accounts */
clear_markers (account->children);
for (lp = account->splits; lp; lp = lp->next)
{
Split *s = lp->data;
Transaction *trans = s->parent;
GNCLot *lot = s->lot;
trans->marker = 0;
if (lot) lot->marker = 0;
}
}
}
/* ================================================================ */
/* Return a unique list of lots that are involved with the listed
* transactions.
*/
static LotList *
create_lot_list_from_trans_list (TransList *trans_list)
{
LotList *lot_list = NULL;
TransList *tnode;
for (tnode=trans_list; tnode; tnode=tnode->next)
{
Transaction *trans = tnode->data;
SplitList *split_list = xaccTransGetSplitList (trans);
SplitList *snode;
for (snode = split_list; snode; snode=snode->next)
{
Split *s = snode->data;
GNCLot *lot = xaccSplitGetLot(s);
if (NULL == lot) continue;
if (g_list_find (lot_list, lot)) continue;
lot_list = g_list_prepend (lot_list, lot);
}
}
return lot_list;
}
/* ================================================================ */
void
gnc_book_partition_pricedb (QofBook *dest_book, QofBook *src_book, QofQuery *query)
{
GNCPriceDB *src_pdb, *dest_pdb;
GList *price_list, *pnode;
if (!src_book || !dest_book || !query) return;
ENTER (" src_book=%p dest_book=%p", src_book, dest_book);
be = src_book->backend;
if (be && be->begin)
src_pdb = gnc_pricedb_get_db (src_book);
dest_pdb = gnc_pricedb_get_db (dest_book);
gnc_pricedb_begin_edit (src_pdb);
gnc_pricedb_begin_edit (dest_pdb);
qof_query_set_book (query, src_book);
price_list = qof_query_run (query);
printf ("duude XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX prices\n");
for (pnode = price_list; pnode; pnode=pnode->next)
{
(*be->begin)(be, GNC_ID_PERIOD, dest_book);
GNCPrice *pr = pnode->data;
gnc_book_insert_price (dest_book, pr);
}
gnc_pricedb_commit_edit (dest_pdb);
gnc_pricedb_commit_edit (src_pdb);
LEAVE (" src_book=%p dest_book=%p", src_book, dest_book);
}
/* ================================================================ */
void
gnc_book_partition_txn (QofBook *dest_book, QofBook *src_book, QofQuery *query)
{
gnc_commodity_table *src_tbl, *dst_tbl;
AccountGroup *src_grp, *dst_grp;
time_t now;
TransList *trans_list, *tnode;
LotList *lot_list, *lnode;
if (!src_book || !dest_book || !query) return;
ENTER (" src_book=%p dest_book=%p", src_book, dest_book);
/* First, copy the book's KVP tree */
/* hack alert -- FIXME -- this should really be a merge, not a
* clobber copy, but I am too lazy to write a kvp merge routine,
@@ -201,6 +534,11 @@ gnc_book_partition (QofBook *dest_book, QofBook *src_book, Query *query)
kvp_frame_delete (dest_book->kvp_data);
dest_book->kvp_data = kvp_frame_copy (src_book->kvp_data);
/* Next, copy the commodity tables */
src_tbl = gnc_commodity_table_get_table (src_book);
dst_tbl = gnc_commodity_table_get_table (dest_book);
gnc_commodity_table_copy (dst_tbl, src_tbl);
/* Next, copy all of the accounts */
/* hack alert -- FIXME -- this should really be a merge, not a
* clobber copy, but I am too lazy to write an account-group merge
@@ -216,34 +554,42 @@ gnc_book_partition (QofBook *dest_book, QofBook *src_book, Query *query)
/* Next, run the query */
xaccAccountGroupBeginEdit (dst_grp);
xaccAccountGroupBeginEdit (src_grp);
xaccQuerySetBook (query, src_book);
split_list = xaccQueryGetSplitsUniqueTrans (query);
qof_query_set_book (query, src_book);
trans_list = qof_query_run (query);
/* And start moving transactions over */
for (snode = split_list; snode; snode=snode->next)
/* Preen: remove open lots/ open trnasactions */
clear_markers (src_grp);
trans_list = trans_list_preen_open_lots (trans_list);
lot_list = create_lot_list_from_trans_list (trans_list);
lot_list = lot_list_preen_open_lots (lot_list);
/* Move closed lots over to destination. Do this before moving
* the txn's, so that the lots don't get trashed. */
for (lnode = lot_list; lnode; lnode = lnode->next)
{
Split *s = snode->data;
Transaction *trans = s->parent;
GNCLot *lot = lnode->data;
gnc_book_insert_lot (dest_book, lot);
}
/* Move the transactions over to the destination book. */
for (tnode = trans_list; tnode; tnode=tnode->next)
{
Transaction *trans = tnode->data;
gnc_book_insert_trans (dest_book, trans);
}
xaccAccountGroupCommitEdit (src_grp);
xaccAccountGroupCommitEdit (dst_grp);
/* make note of the sibling books */
/* Make note of the sibling books */
now = time(0);
gnc_kvp_gemini (src_book->kvp_data, NULL, &dest_book->guid, now);
gnc_kvp_gemini (dest_book->kvp_data, NULL, &src_book->guid, now);
if (be && be->commit)
{
(*be->commit)(be, GNC_ID_PERIOD, dest_book);
}
gnc_kvp_gemini (src_book->kvp_data, now, "book_guid", &dest_book->guid, NULL);
gnc_kvp_gemini (dest_book->kvp_data, now, "book_guid", &src_book->guid, NULL);
LEAVE (" ");
}
/* ================================================================ */
/* find nearest equity account */
/* Find nearest equity account */
static Account *
find_nearest_equity_acct (Account *acc)
@@ -252,7 +598,7 @@ find_nearest_equity_acct (Account *acc)
AccountGroup *parent;
Account *next_up, *candidate;
/* see if we can find an equity account that is peered to this account */
/* See if we can find an equity account that is peered to this account */
parent = xaccAccountGetParent (acc);
g_return_val_if_fail (parent, NULL);
@@ -292,7 +638,7 @@ find_nearest_equity_acct (Account *acc)
}
/* ================================================================ */
/* traverse all accounts, get account balances */
/* Traverse all accounts, get account balances */
static void
add_closing_balances (AccountGroup *closed_grp,
@@ -307,48 +653,38 @@ add_closing_balances (AccountGroup *closed_grp,
if (!closed_grp) return;
ENTER (" enter=%s post=%s desc=%s", gnc_print_date(*date_entered),
gnc_print_date (*post_date), desc);
xaccAccountBeginEdit (equity_account);
/* walk accounts in closed book */
/* Walk accounts in closed book */
acc_list = xaccGroupGetAccountList (closed_grp);
for (node=acc_list; node; node=node->next)
{
KvpFrame *cwd;
KvpValue *vvv;
Account *twin;
AccountGroup *childs;
Account * candidate = (Account *) node->data;
GNCAccountType tip = xaccAccountGetType (candidate);
/* find the peer account of this account in the open book */
/* Find the peer account of this account in the open book */
twin = xaccAccountLookupTwin (candidate, open_book);
/* -------------------------------- */
/* add KVP to open account, indicating the progenitor
/* Add KVP to open account, indicating the progenitor
* of this account. */
xaccAccountBeginEdit (twin);
cwd = xaccAccountGetSlots (twin);
cwd = kvp_frame_get_frame_slash (cwd, "/book/");
vvv = kvp_value_new_guid (xaccAccountGetGUID (candidate));
kvp_frame_set_slot_nc (cwd, "prev-acct", vvv);
vvv = kvp_value_new_guid (&closed_book->guid);
kvp_frame_set_slot_nc (cwd, "prev-book", vvv);
kvp_frame_set_guid (cwd, "/book/prev-acct", xaccAccountGetGUID (candidate));
kvp_frame_set_guid (cwd, "/book/prev-book", &closed_book->guid);
xaccAccountSetSlots_nc (twin, twin->kvp_data);
/* -------------------------------- */
/* add KVP to closed account, indicating where
/* Add KVP to closed account, indicating where
* the next book is. */
xaccAccountBeginEdit (candidate);
cwd = xaccAccountGetSlots (candidate);
cwd = kvp_frame_get_frame_slash (cwd, "/book/");
vvv = kvp_value_new_guid (&open_book->guid);
kvp_frame_set_slot_nc (cwd, "next-book", vvv);
vvv = kvp_value_new_guid (xaccAccountGetGUID (twin));
kvp_frame_set_slot_nc (cwd, "next-acct", vvv);
kvp_frame_set_guid (cwd, "/book/next-book", &open_book->guid);
kvp_frame_set_guid (cwd, "/book/next-acct", xaccAccountGetGUID (twin));
xaccAccountSetSlots_nc (candidate, candidate->kvp_data);
@@ -357,72 +693,76 @@ add_closing_balances (AccountGroup *closed_grp,
* and income or expense or equity account */
if ((INCOME != tip) && (EXPENSE != tip) && (EQUITY != tip))
{
Split *se, *st;
Transaction *trans;
Account *equity;
gnc_numeric baln;
baln = xaccAccountGetBalance (candidate);
/* find the equity account into which we'll poke the
* balancing transaction */
if (NULL == equity_account)
/* Don't bother with creating the equity balance if its zero */
if (FALSE == gnc_numeric_zero_p(baln))
{
equity = find_nearest_equity_acct (twin);
Split *se, *st;
Transaction *trans;
Account *equity;
/* Find the equity account into which we'll poke the
* balancing transaction */
if (NULL == equity_account)
{
equity = find_nearest_equity_acct (twin);
xaccAccountBeginEdit (equity);
}
else
{
equity = equity_account;
}
/* -------------------------------- */
/* Create the balancing transaction */
trans = xaccMallocTransaction (open_book);
xaccTransBeginEdit (trans);
xaccTransSetDatePostedTS (trans, post_date);
xaccTransSetDateEnteredTS (trans, date_entered);
xaccTransSetDescription (trans, desc);
xaccTransSetCurrency (trans, xaccAccountGetCommodity(equity));
st = xaccMallocSplit(open_book);
xaccTransAppendSplit(trans, st);
xaccAccountInsertSplit (twin, st);
se = xaccMallocSplit(open_book);
xaccTransAppendSplit(trans, se);
xaccAccountInsertSplit (equity, se);
xaccSplitSetAmount (st, baln);
xaccSplitSetValue (st, baln);
xaccSplitSetAmount (se, gnc_numeric_neg(baln));
xaccSplitSetValue (se, gnc_numeric_neg(baln));
/* Add KVP data showing where the balancing
* transaction came from */
cwd = xaccTransGetSlots (trans);
kvp_frame_set_guid (cwd, "/book/closed-book", &closed_book->guid);
kvp_frame_set_guid (cwd, "/book/closed-acct", xaccAccountGetGUID(candidate));
xaccTransCommitEdit (trans);
if (NULL == equity_account)
{
xaccAccountCommitEdit (equity);
}
/* -------------------------------- */
/* Add KVP to closed account, indicating where the
* balance was carried forward to. */
cwd = xaccAccountGetSlots (candidate);
kvp_frame_set_guid (cwd, "/book/balancing-trans", xaccTransGetGUID(trans));
}
else
{
equity = equity_account;
}
/* -------------------------------- */
/* create the balancing transaction */
trans = xaccMallocTransaction (open_book);
xaccTransBeginEdit (trans);
st = xaccMallocSplit(open_book);
xaccAccountInsertSplit (twin, st);
xaccTransAppendSplit(trans, st);
se = xaccMallocSplit(open_book);
xaccAccountInsertSplit (equity, se);
xaccTransAppendSplit(trans, se);
xaccSplitSetValue (st, baln);
xaccSplitSetValue (se, gnc_numeric_neg(baln));
xaccTransSetDatePostedTS (trans, post_date);
xaccTransSetDateEnteredTS (trans, date_entered);
xaccTransSetDescription (trans, desc);
xaccTransSetCurrency (trans, xaccAccountGetCommodity(equity));
/* add KVP data showing where the balancing
* transaction came from */
cwd = xaccTransGetSlots (trans);
cwd = kvp_frame_get_frame_slash (cwd, "/book/");
vvv = kvp_value_new_guid (&closed_book->guid);
kvp_frame_set_slot_nc (cwd, "closed-book", vvv);
vvv = kvp_value_new_guid (xaccAccountGetGUID(candidate));
kvp_frame_set_slot_nc (cwd, "closed-acct", vvv);
xaccTransCommitEdit (trans);
/* -------------------------------- */
/* add KVP to closed account, indicating where the
* balance was carried forward to. */
cwd = xaccAccountGetSlots (candidate);
cwd = kvp_frame_get_frame_slash (cwd, "/book/");
vvv = kvp_value_new_guid (xaccTransGetGUID(trans));
kvp_frame_set_slot_nc (cwd, "balancing-trans", vvv);
}
/* we left an open dangling above ... */
/* We left an open dangling above ... */
xaccAccountCommitEdit (candidate);
xaccAccountCommitEdit (twin);
/* recurse down to the children */
/* Recurse down to the children */
childs = xaccAccountGetChildren(candidate);
if (childs)
{
@@ -433,61 +773,99 @@ add_closing_balances (AccountGroup *closed_grp,
post_date, date_entered, desc);
}
}
xaccAccountCommitEdit (equity_account);
LEAVE (" ");
}
/* ================================================================ */
/* split a book into two by date */
static void
period_begin_edit (QofBook *src_book, QofBook *dest_book)
{
QofBackend *be;
be = src_book->backend;
if (be && be->begin)
{
(*be->begin)(be, GNC_ID_PERIOD, dest_book);
}
}
static void
period_commit_edit (QofBook *src_book, QofBook *dest_book)
{
QofBackend *be;
be = src_book->backend;
if (be && be->commit)
{
(*be->commit)(be, GNC_ID_PERIOD, dest_book);
}
}
/* ================================================================ */
/* Split a book into two by date */
QofBook *
gnc_book_close_period (QofBook *existing_book, Timespec calve_date,
Account *equity_account,
const char * memo)
{
Query *query;
QofQuery *txn_query, *prc_query;
QofQueryPredData *pred_data;
GSList *param_list;
QofBook *closing_book;
KvpFrame *exist_cwd, *partn_cwd;
KvpValue *vvv;
Timespec ts;
if (!existing_book) return NULL;
ENTER (" date=%s memo=%s", gnc_print_date(calve_date), memo);
/* Get all transactions that are *earlier* than the calve date,
* and put them in the new book. */
query = xaccMallocQuery();
xaccQueryAddDateMatchTS (query, FALSE, calve_date,
TRUE, calve_date,
QOF_QUERY_AND);
/* Setup closuing book */
closing_book = qof_book_new();
qof_book_set_backend (closing_book, existing_book->backend);
closing_book->book_open = 'n';
gnc_book_partition (closing_book, existing_book, query);
xaccFreeQuery (query);
period_begin_edit (existing_book, closing_book);
/* Get all transactions that are *earlier* than the calve date,
* and put them in the new book. */
txn_query = qof_query_create_for (GNC_ID_TRANS);
pred_data = qof_query_date_predicate (QOF_COMPARE_LTE,
QOF_DATE_MATCH_NORMAL,
calve_date);
param_list = qof_query_build_param_list (TRANS_DATE_POSTED, NULL);
qof_query_add_term (txn_query, param_list, pred_data, QOF_QUERY_FIRST_TERM);
gnc_book_partition_txn (closing_book, existing_book, txn_query);
qof_query_destroy (txn_query);
/* Move prices over too */
prc_query = qof_query_create_for (GNC_ID_PRICE);
pred_data = qof_query_date_predicate (QOF_COMPARE_LTE,
QOF_DATE_MATCH_NORMAL,
calve_date);
param_list = qof_query_build_param_list (PRICE_DATE, NULL);
qof_query_add_term (prc_query, param_list, pred_data, QOF_QUERY_FIRST_TERM);
gnc_book_partition_pricedb (closing_book, existing_book, prc_query);
qof_query_destroy (prc_query);
/* Now add the various identifying kvp's */
/* cwd == 'current working directory' */
exist_cwd = kvp_frame_get_frame_slash (existing_book->kvp_data, "/book/");
partn_cwd = kvp_frame_get_frame_slash (closing_book->kvp_data, "/book/");
exist_cwd = existing_book->kvp_data;
partn_cwd = closing_book->kvp_data;
/* Mark the boundary date between the books */
vvv = kvp_value_new_timespec (calve_date);
kvp_frame_set_slot_nc (exist_cwd, "open-date", vvv);
kvp_frame_set_slot_nc (partn_cwd, "close-date", vvv);
kvp_frame_set_timespec (exist_cwd, "/book/open-date", calve_date);
kvp_frame_set_timespec (partn_cwd, "/book/close-date", calve_date);
/* Mark partition as being closed */
ts.tv_sec = time(0);
ts.tv_nsec = 0;
vvv = kvp_value_new_timespec (ts);
kvp_frame_set_slot_nc (partn_cwd, "log-date", vvv);
kvp_frame_set_timespec (partn_cwd, "/book/log-date", ts);
/* Set up pointers to each book from the other. */
vvv = kvp_value_new_guid (&existing_book->guid);
kvp_frame_set_slot_nc (partn_cwd, "next-book", vvv);
vvv = kvp_value_new_guid (&closing_book->guid);
kvp_frame_set_slot_nc (exist_cwd, "prev-book", vvv);
kvp_frame_set_guid (partn_cwd, "/book/next-book", &existing_book->guid);
kvp_frame_set_guid (exist_cwd, "/book/prev-book", &closing_book->guid);
/* add in transactions to equity accounts that will
* hold the colsing balances */
@@ -495,6 +873,9 @@ gnc_book_close_period (QofBook *existing_book, Timespec calve_date,
existing_book, closing_book,
equity_account,
&calve_date, &ts, memo);
period_commit_edit (existing_book, closing_book);
LEAVE (" ");
return closing_book;
}

View File

@@ -1,13 +1,28 @@
/********************************************************************\
* Period.h -- Implement accounting Periods *
* *
* 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 *
* 59 Temple Place - Suite 330 Fax: +1-617-542-2652 *
* Boston, MA 02111-1307, USA gnu@gnu.org *
\********************************************************************/
/** @addtogroup Engine
@{ */
/** @file Period.h
*
* Implement accounting periods.
*
* CAUTION: this is currently a semi-functional, untested implementation
* of the design described in src/doc/book.txt
*
* HISTORY:
* created by Linas Vepstas November 2001
* Copyright (c) 2001 Linas Vepstas <linas@linas.org>
* @breif Implement accounting periods, as per design in src/doc/books.txt
* @author Copyright (c) 2001,2003 Linas Vepstas <linas@linas.org>
*/
@@ -15,7 +30,7 @@
#define XACC_PERIOD_H
#include "gnc-engine.h"
#include "Query.h"
#include "qofquery.h"
#include "qofbook.h"
@@ -81,36 +96,35 @@
* /book/prev-acct GUID of twin of this account in the closed book.
* /book/prev-book GUID of previous book (the closed book)
*
-- hack alert --
-- Need to also split up the price db too.
-- Need to make copies of SX, and the like ...
-- Need to move lots too
-- hack alert -- feature request:
have some way of remembering the quickfill text from older books...
-- hack alert -- should not allow closed books to have unreconciled
transactions ???
*/
QofBook * gnc_book_close_period (QofBook *, Timespec,
Account *equity_acct,
const char *memo);
/** The gnc_book_partition() uses the result of the indicated query
/** The gnc_book_partition_txn() uses the result of the indicated query
* to move a set of transactions from the "src" book to the "dest"
* book. Before moving the transactions, it will first place a
* copy of all of the accounts in "src" into "dest". This is done
* in order to ensure that all of the moved transactions will have
* the corrrect set of accounts to reference. The transactions
* that will be moved are precisely those specified by the query.
* Any query will work to partition a book; however, its expected
* that this routine will mostly serve as a utility to break up a
* book into accounting periods.
* Any query that returns a list of transactions will work to
* partition a book; however, its expected that this routine will
* mostly serve as a utility to break up a book into accounting
* periods.
*
* This routine intentionally does not copy scheduled/recurring
* transactions.
*
* This routine will also copy closed lots to the destination book.
* NOTICE:
* It will not copy open lots, nor will it copy lots that have
* lead to transactions that contains splits in other open lots.
* Leaving behind open lots is exactly what is needed for closing
* books, but it means that gnc_book_partition() is not really
* a 'general purpose' function. The way to fix this would be to
* weed out open lots by constructing the query correctly.
*
* When an account is copied, the copy is issued a new GUID.
* The GUID of its sibling is placed in the 'gemini' KVP value
* (See kvp_doc.txt for more detail). Transactions and splits
@@ -125,7 +139,14 @@ QofBook * gnc_book_close_period (QofBook *, Timespec,
* For the current usage, this bug aint important, and I'm too
* lazy to fix it.
*/
void gnc_book_partition (QofBook *dest, QofBook *src, Query *);
void gnc_book_partition_txn (QofBook *dest, QofBook *src, QofQuery *);
/** The gnc_book_partition_pricedb() routine uses te result of the
* indicated query to move a set of prices from the "src" book
* to the "dest" book. The query passed into it must be set up
* to return a list of prices.
*/
void gnc_book_partition_pricedb (QofBook *dest, QofBook *src, QofQuery *);
/** The gnc_book_insert_trans_clobber() routine takes an existing
* transaction that is located in one book, and moves it to
@@ -137,14 +158,28 @@ void gnc_book_partition (QofBook *dest, QofBook *src, Query *);
* both books (and can be located with the standard twining
* proceedure).
*
* Note that this routine does *not* move the lots that any
* of the splits might belong to. These must be moved sepearately.
* Note that one must take care when moving a transaction, so
* that any associated lots don't end up hamstrung across two
* different books.
*
* The gnc_book_insert_trans() routine does the same as the above,
* except that it doesn't actually clobber the transaction: it
* merely moves the transaction and split GUID's to the new
* books' entity tables, and not much else.
*
* The gnc_book_insert_lot() routine, as above, but for lots ...
*/
void gnc_book_insert_trans (QofBook *book, Transaction *trans);
void gnc_book_insert_trans_clobber (QofBook *book, Transaction *trans);
#endif /* XACC_PERIOD_H */
void gnc_book_insert_lot (QofBook *book, GNCLot *lot);
void gnc_book_insert_lot_clobber (QofBook *book, GNCLot *lot);
void gnc_book_insert_price (QofBook *book, GNCPrice *prc);
void gnc_book_insert_price_clobber (QofBook *book, GNCPrice *prc);
#endif /* XACC_PERIOD_H */
/** @} */

View File

@@ -35,11 +35,12 @@
#include "gnc-engine-util.h"
#include "gnc-numeric.h"
#include "gnc-lot.h"
#include "gnc-trace.h"
#include "Account.h"
#include "Query.h"
#include "Transaction.h"
#include "TransactionP.h"
#include "gnc-lot.h"
static short module = MOD_QUERY;

View File

@@ -58,11 +58,8 @@ typedef QofQuery Query;
* The xaccQueryNumTerms() routine returns the total number of terms in the query.
*/
// void xaccQueryPurgeTerms(Query * q, pd_type_t type);
// gboolean xaccQueryHasTermType(Query * q, pd_type_t type);
#define xaccQueryHasTerms qof_query_has_terms
#define xaccQueryNumTerms qof_query_num_terms
// #define xaccQueryGetTerms qof_query_get_terms
#define xaccQuerySetSortIncreasing qof_query_set_sort_increasing
@@ -78,6 +75,8 @@ typedef enum {
} query_txn_match_t;
/* After the query has been set up, call one of these to run the query.
* XXX The routines below should be replaced by a query
* that explicitly asks for a list of the desied item.
*
* The xaccQueryGetSplits() routine returns all splits matching the
* query. Any given split will appear at most once in the result;
@@ -168,7 +167,7 @@ void xaccQueryAddNumericMatch (Query *q, gnc_numeric amount,
((bal) ? QOF_COMPARE_EQUAL : QOF_COMPARE_NEQ), (op), \
SPLIT_TRANS, TRANS_IMBALANCE, NULL)
/* The DateMatch queries match transactions whose posted date
/** The DateMatch queries match transactions whose posted date
* is in a date range. If use_start is TRUE, then a matching
* posted date will be greater than the start date. If
* use_end is TRUE, then a match occurs for posted dates earlier
@@ -205,7 +204,7 @@ void xaccQueryAddGUIDMatch(Query * q, const GUID *guid,
void xaccQueryAddGUIDMatchGL (QofQuery *q, GList *param_list,
GUID guid, QofQueryOp op);
/* given kvp value is on right side of comparison */
/** given kvp value is on right side of comparison */
void xaccQueryAddKVPMatch(Query *q, GSList *path, const KvpValue *value,
QofQueryCompare how, QofIdType id_type,
QofQueryOp op);

View File

@@ -37,6 +37,7 @@
#include "gnc-engine.h"
#include "gnc-engine-util.h"
#include "gnc-event-p.h"
#include "gnc-trace.h"
#include "guid.h"
#include "messages.h"
#include "qofbook.h"

View File

@@ -52,6 +52,7 @@
#include "gnc-engine-util.h"
#include "messages.h"
#include "gnc-commodity.h"
#include "gnc-trace.h"
static short module = MOD_SCRUB;
@@ -250,9 +251,9 @@ xaccSplitScrub (Split *split)
PINFO ("Adjusted split with mismatched values, desc=\"%s\" memo=\"%s\""
" old amount %s %s, new amount %s",
trans->description, split->memo,
gnc_numeric_to_string (split->amount),
gnc_numeric_to_string (xaccSplitGetAmount(split)),
gnc_commodity_get_mnemonic (currency),
gnc_numeric_to_string (split->value));
gnc_numeric_to_string (xaccSplitGetValue(split)));
xaccTransBeginEdit (trans);
xaccSplitSetAmount (split, value);
@@ -409,6 +410,118 @@ xaccTransScrubImbalance (Transaction *trans, AccountGroup *root,
}
}
/* ================================================================ */
/* The xaccTransFindCommonCurrency () method returns
* a gnc_commodity indicating a currency denomination that all
* of the splits in this transaction have in common, using the
* old/obsolete currency/security fields of the split accounts.
*/
static gnc_commodity *
FindCommonExclSCurrency (SplitList *splits,
gnc_commodity * ra, gnc_commodity * rb,
Split *excl_split)
{
GList *node;
if (!splits) return NULL;
for (node = splits; node; node = node->next)
{
Split *s = node->data;
gnc_commodity * sa, * sb;
if (s == excl_split) continue;
g_return_val_if_fail (s->acc, NULL);
sa = DxaccAccountGetCurrency (s->acc);
sb = xaccAccountGetCommodity (s->acc);
if (ra && rb) {
int aa = !gnc_commodity_equiv(ra,sa);
int ab = !gnc_commodity_equiv(ra,sb);
int ba = !gnc_commodity_equiv(rb,sa);
int bb = !gnc_commodity_equiv(rb,sb);
if ( (!aa) && bb) rb = NULL;
else
if ( (!ab) && ba) rb = NULL;
else
if ( (!ba) && ab) ra = NULL;
else
if ( (!bb) && aa) ra = NULL;
else
if ( aa && bb && ab && ba ) { ra = NULL; rb = NULL; }
if (!ra) { ra = rb; rb = NULL; }
}
else
if (ra && !rb) {
int aa = !gnc_commodity_equiv(ra,sa);
int ab = !gnc_commodity_equiv(ra,sb);
if ( aa && ab ) ra = NULL;
}
if ((!ra) && (!rb)) return NULL;
}
return (ra);
}
/* This is the wrapper for those calls (i.e. the older ones) which
* don't exclude one split from the splitlist when looking for a
* common currency.
*/
static gnc_commodity *
FindCommonCurrency (GList *splits, gnc_commodity * ra, gnc_commodity * rb)
{
return FindCommonExclSCurrency(splits, ra, rb, NULL);
}
static gnc_commodity *
xaccTransFindOldCommonCurrency (Transaction *trans, QofBook *book)
{
gnc_commodity *ra, *rb, *retval;
Split *split;
if (!trans) return NULL;
if (trans->splits == NULL) return NULL;
g_return_val_if_fail (book, NULL);
split = trans->splits->data;
if (!split || NULL == split->acc) return NULL;
ra = DxaccAccountGetCurrency (split->acc);
rb = xaccAccountGetCommodity (split->acc);
retval = FindCommonCurrency (trans->splits, ra, rb);
/* Compare this value to what we think should be the 'right' value */
if (!trans->common_currency)
{
trans->common_currency = retval;
}
else if (!gnc_commodity_equiv (retval,trans->common_currency))
{
PWARN ("expected common currency %s but found %s\n",
gnc_commodity_get_unique_name (trans->common_currency),
gnc_commodity_get_unique_name (retval));
}
if (NULL == retval)
{
/* In every situation I can think of, this routine should return
* common currency. So make note of this ... */
PWARN ("unable to find a common currency, and that is strange.");
}
return retval;
}
/* ================================================================ */
void
@@ -491,14 +604,15 @@ xaccTransScrubCurrency (Transaction *trans)
PWARN ("Adjusted split with mismatched values, desc=\"%s\" memo=\"%s\""
" old amount %s %s, new amount %s",
trans->description, sp->memo,
gnc_numeric_to_string (sp->amount),
gnc_numeric_to_string (xaccSplitGetAmount(sp)),
gnc_commodity_get_mnemonic (currency),
gnc_numeric_to_string (sp->value));
gnc_numeric_to_string (xaccSplitGetValue(sp)));
xaccTransBeginEdit (trans);
xaccSplitSetAmount (sp, sp->value);
xaccSplitSetAmount (sp, xaccSplitGetValue(sp));
xaccTransCommitEdit (trans);
}
/*else {
/*else
{
PINFO ("Ok: Split '%s' Amount %s %s, value %s %s",
xaccSplitGetMemo (sp),
gnc_numeric_to_string (amount),
@@ -543,11 +657,22 @@ xaccAccountScrubCommodity (Account *account)
/* ================================================================ */
static gboolean
static void
xaccAccountDeleteOldData (Account *account)
{
if (!account) return;
kvp_frame_set_slot_nc (account->kvp_data, "old-currency", NULL);
kvp_frame_set_slot_nc (account->kvp_data, "old-security", NULL);
kvp_frame_set_slot_nc (account->kvp_data, "old-currency-scu", NULL);
kvp_frame_set_slot_nc (account->kvp_data, "old-security-scu", NULL);
}
static int
scrub_trans_currency_helper (Transaction *t, gpointer data)
{
xaccTransScrubCurrency (t);
return TRUE;
return 0;
}
static gpointer
@@ -603,7 +728,7 @@ move_quote_source (Account *account, gpointer data)
tz = dxaccAccountGetQuoteTZ(account);
PINFO("to %8s from %s", gnc_commodity_get_mnemonic(com),
xaccAccountGetName(account));
xaccAccountGetName(account));
gnc_commodity_set_quote_flag(com, TRUE);
quote_source = gnc_quote_source_lookup_by_internal(source);
if (!quote_source)
@@ -633,7 +758,7 @@ xaccGroupScrubQuoteSources (AccountGroup *group, gnc_commodity_table *table)
xaccAccountGroupBeginEdit (group);
xaccGroupForEachAccount (group, move_quote_source,
GINT_TO_POINTER(new_style), TRUE);
GINT_TO_POINTER(new_style), TRUE);
xaccAccountGroupCommitEdit (group);
LEAVE("Migration done");
}

View File

@@ -1,4 +1,6 @@
/********************************************************************\
* Scrub2.c -- Convert Stock Accounts to use Lots *
* *
* 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 *
@@ -40,91 +42,22 @@
#include "TransactionP.h"
#include "Scrub2.h"
#include "ScrubP.h"
#include "cap-gains.h"
#include "gnc-engine.h"
#include "gnc-engine-util.h"
#include "gnc-lot.h"
#include "gnc-lot-p.h"
#include "gnc-trace.h"
#include "kvp-util-p.h"
#include "messages.h"
#include "policy-p.h"
static short module = MOD_LOT;
/* ============================================================== */
gboolean
xaccAccountHasTrades (Account *acc)
{
gnc_commodity *acc_comm;
SplitList *node;
if (!acc) return FALSE;
acc_comm = acc->commodity;
for (node=acc->splits; node; node=node->next)
{
Split *s = node->data;
Transaction *t = s->parent;
if (acc_comm != t->common_currency) return TRUE;
}
return FALSE;
}
/* ============================================================== */
struct early_lot_s
{
GNCLot *lot;
Timespec ts;
int (*numeric_pred)(gnc_numeric);
};
static gpointer earliest_helper (GNCLot *lot, gpointer user_data)
{
struct early_lot_s *els = user_data;
Split *s;
Transaction *trans;
gnc_numeric bal;
if (gnc_lot_is_closed (lot)) return NULL;
/* We want a lot whose balance is of the correct sign */
bal = gnc_lot_get_balance (lot);
if (0 == (els->numeric_pred) (bal)) return NULL;
s = gnc_lot_get_earliest_split (lot);
trans = s->parent;
if ((els->ts.tv_sec > trans->date_posted.tv_sec) ||
((els->ts.tv_sec == trans->date_posted.tv_sec) &&
(els->ts.tv_nsec > trans->date_posted.tv_nsec)))
{
els->ts = trans->date_posted;
els->lot = lot;
}
return NULL;
}
GNCLot *
xaccAccountFindEarliestOpenLot (Account *acc, gnc_numeric sign)
{
struct early_lot_s es;
es.lot = NULL;
es.ts.tv_sec = 10000000LL * ((long long) LONG_MAX);
es.ts.tv_nsec = 0;
if (gnc_numeric_positive_p(sign)) es.numeric_pred = gnc_numeric_negative_p;
else es.numeric_pred = gnc_numeric_positive_p;
xaccAccountForEachLot (acc, earliest_helper, &es);
return es.lot;
}
/* ============================================================== */
void
xaccAccountScrubLots (Account *acc)
xaccAccountAssignLots (Account *acc)
{
SplitList *node;
@@ -135,270 +68,68 @@ xaccAccountScrubLots (Account *acc)
/* Loop over all splits, and make sure that every split
* belongs to some lot. If a split does not belong to
* any lots, its is placed into the earliest possible
* lot (thus enforcing FIFO accounting rules).
* any lots, poke it into one.
*/
restart_loop:
for (node=acc->splits; node; node=node->next)
{
gboolean splits_added = FALSE;
Split * split = node->data;
GNCLot *lot = split->lot;
/* If this split belongs to a lot, its good. */
if (lot) continue;
/* If we are here, this split does not belong to any lot.
* Lets put it in the earliest one we can find. This
* block is written in the form of a while loop, since we
* may have to bust a split across several lots.
*/
while (split)
{
PINFO ("have split amount=%s", gnc_numeric_to_string (split->amount));
lot = xaccAccountFindEarliestOpenLot (acc, split->amount);
if (lot)
{
/* If the amount is smaller than open balance ... */
gnc_numeric baln = gnc_lot_get_balance (lot);
int cmp = gnc_numeric_compare (split->amount, baln);
PINFO ("found open lot with baln=%s", gnc_numeric_to_string (baln));
/* cmp == +1 if amt > baln */
if (0 < cmp)
{
Split * new_split;
gnc_numeric amt_a, amt_b, amt_tot;
gnc_numeric val_a, val_b, val_tot;
Transaction *trans;
Timespec ts;
trans = split->parent;
xaccTransBeginEdit (trans);
amt_tot = split->amount;
amt_a = gnc_numeric_neg (baln);
amt_b = gnc_numeric_sub_fixed (amt_tot, amt_a);
PINFO ("XXXXXXXXXXXXXXXX splitting split ");
/* Compute the value so that it holds in the same proportion:
* i.e. so that (amt_a / amt_tot) = (val_a / val_tot)
*/
val_tot = split->value;
val_a = gnc_numeric_mul (amt_a, val_tot, GNC_DENOM_AUTO, GNC_RND_NEVER);
val_a = gnc_numeric_div (val_a, amt_tot, gnc_numeric_denom(val_tot), GNC_DENOM_EXACT);
val_b = gnc_numeric_sub_fixed (val_tot, val_a);
xaccSplitSetAmount (split, amt_a);
xaccSplitSetValue (split, val_a);
/* Adding this split will have the effect of closing this lot,
* because the new balance should be precisely zero. */
gnc_lot_add_split (lot, split);
/* put the remainder of the balance into a new split, which is
* in other respects just a clone of this one */
/* XXX FIXME: we should add some kvp markup to indicate that these
* two splits used to be one before being 'split' */
new_split = xaccMallocSplit (acc->book);
/* Copy most of teh split attributes */
xaccSplitSetMemo (new_split, xaccSplitGetMemo (split));
xaccSplitSetAction (new_split, xaccSplitGetAction (split));
xaccSplitSetReconcile (new_split, xaccSplitGetReconcile (split));
ts = xaccSplitRetDateReconciledTS (split);
xaccSplitSetDateReconciledTS (new_split, &ts);
/* Copying the KVP tree seems like the right thing to do,
* this is potentially dangerous, depending on how other
* users use it.*/
xaccSplitSetSlots_nc (new_split, kvp_frame_copy(xaccSplitGetSlots (split)));
xaccSplitSetAmount (new_split, amt_b);
xaccSplitSetValue (new_split, val_b);
xaccAccountInsertSplit (acc, new_split);
xaccTransAppendSplit (trans, new_split);
xaccTransCommitEdit (trans);
split = new_split;
splits_added = TRUE;
}
else
{
gnc_lot_add_split (lot, split);
split = NULL;
PINFO ("added split to lot, new lot baln=%s",
gnc_numeric_to_string (gnc_lot_get_balance(lot)));
}
}
else
{
/* No lot was found. Start a new lot */
PINFO ("start new lot");
lot = gnc_lot_new (acc->book);
gnc_lot_add_split (lot, split);
split = NULL;
}
}
if (splits_added) goto restart_loop;
/* If already in lot, then no-op */
if (split->lot) continue;
if (xaccSplitAssign (split)) goto restart_loop;
}
xaccAccountCommitEdit (acc);
LEAVE ("acc=%s", acc->accountName);
}
/* ============================================================== */
static Timespec
gnc_lot_get_close_date (GNCLot *lot)
{
Split *s = gnc_lot_get_latest_split (lot);
Transaction *trans = s->parent;
return trans->date_posted;
}
/* ============================================================== */
/* Similar to GetOrMakeAccount, but different in important ways */
static Account *
GetOrMakeLotOrphanAccount (AccountGroup *root, gnc_commodity * currency)
{
char * accname;
Account * acc;
g_return_val_if_fail (root, NULL);
/* build the account name */
if (!currency)
{
PERR ("No currency specified!");
return NULL;
}
accname = g_strconcat (_("Orphaned Gains"), "-",
gnc_commodity_get_mnemonic (currency), NULL);
/* See if we've got one of these going already ... */
acc = xaccGetAccountFromName (root, accname);
if (acc == NULL)
{
/* Guess not. We'll have to build one. */
acc = xaccMallocAccount (root->book);
xaccAccountBeginEdit (acc);
xaccAccountSetName (acc, accname);
xaccAccountSetCommodity (acc, currency);
xaccAccountSetType (acc, INCOME);
xaccAccountSetDescription (acc, _("Realized Gain/Loss"));
xaccAccountSetNotes (acc,
_("Realized Gains or Losses from\n"
"Commodity or Trading Accounts\n"
"that haven't been recorded elsewhere.\n"));
/* Hang the account off the root. */
xaccGroupInsertAccount (root, acc);
xaccAccountCommitEdit (acc);
}
g_free (accname);
return acc;
}
/* ============================================================== */
void
xaccAccountSetDefaultGainAccount (Account *acc, Account *gain_acct)
{
KvpFrame *cwd;
KvpValue *vvv;
const char * cur_name;
if (!acc || !gain_acct) return;
cwd = xaccAccountGetSlots (acc);
cwd = kvp_frame_get_frame_slash (cwd, "/lot-mgmt/gains-act/");
/* Accounts are indexed by thier unique currency name */
cur_name = gnc_commodity_get_unique_name (acc->commodity);
xaccAccountBeginEdit (acc);
vvv = kvp_value_new_guid (xaccAccountGetGUID (gain_acct));
kvp_frame_set_slot_nc (cwd, cur_name, vvv);
xaccAccountSetSlots_nc (acc, acc->kvp_data);
xaccAccountCommitEdit (acc);
}
/* ============================================================== */
Account *
xaccAccountGetDefaultGainAccount (Account *acc, gnc_commodity * currency)
{
Account *gain_acct = NULL;
KvpFrame *cwd;
KvpValue *vvv;
GUID * gain_acct_guid;
const char * cur_name;
if (!acc || !currency) return NULL;
cwd = xaccAccountGetSlots (acc);
cwd = kvp_frame_get_frame_slash (cwd, "/lot-mgmt/gains-act/");
/* Accounts are indexed by thier unique currency name */
cur_name = gnc_commodity_get_unique_name (currency);
vvv = kvp_frame_get_slot (cwd, cur_name);
gain_acct_guid = kvp_value_get_guid (vvv);
gain_acct = xaccAccountLookup (gain_acct_guid, acc->book);
return gain_acct;
}
/* ============================================================== */
/* Functionally identical to the following:
* if (!xaccAccountGetDefaultGainAccount()) xaccAccountSetDefaultGainAccount ();
* except that it saves a few cycles.
/** The xaccLotFill() routine attempts to assign splits to the
* indicated lot until the lot balance goes to zero, or until
* there are no suitable (i.e. unassigned) splits left in the
* account. It uses the default accounting policy to choose
* the splits to fill out the lot.
*/
static Account *
GetOrMakeGainAcct (Account *acc, gnc_commodity * currency)
void
xaccLotFill (GNCLot *lot)
{
Account *gain_acct = NULL;
KvpFrame *cwd;
KvpValue *vvv;
GUID * gain_acct_guid;
const char * cur_name;
gnc_numeric lot_baln;
Account *acc;
cwd = xaccAccountGetSlots (acc);
cwd = kvp_frame_get_frame_slash (cwd, "/lot-mgmt/gains-act/");
if (!lot) return;
acc = lot->account;
/* Accounts are indexed by thier unique currency name */
cur_name = gnc_commodity_get_unique_name (currency);
vvv = kvp_frame_get_slot (cwd, cur_name);
gain_acct_guid = kvp_value_get_guid (vvv);
ENTER ("acc=%s", acc->accountName);
gain_acct = xaccAccountLookup (gain_acct_guid, acc->book);
/* If balance already zero, we have nothing to do. */
lot_baln = gnc_lot_get_balance (lot);
if (gnc_numeric_zero_p (lot_baln)) return;
/* If there is no default place to put gains/losses
* for this account, then create such a place */
if (NULL == gain_acct)
{
AccountGroup *root;
xaccAccountBeginEdit (acc);
xaccAccountBeginEdit (acc);
root = xaccAccountGetRoot(acc);
gain_acct = GetOrMakeLotOrphanAccount (root, currency);
/* Loop until we've filled up the lot, (i.e. till the
* balance goes to zero) or there are no splits left. */
while (1)
{
Split *split, *subsplit;
vvv = kvp_value_new_guid (xaccAccountGetGUID (gain_acct));
kvp_frame_set_slot_nc (cwd, cur_name, vvv);
xaccAccountSetSlots_nc (acc, acc->kvp_data);
xaccAccountCommitEdit (acc);
split = FIFOPolicyGetSplit (lot, NULL);
subsplit = xaccSplitAssignToLot (split, lot);
if (subsplit == split)
{
PERR ("Accounting Policy gave us a split that "
"doesn't fit into this lot");
break;
}
}
return gain_acct;
lot_baln = gnc_lot_get_balance (lot);
if (gnc_numeric_zero_p (lot_baln)) break;
}
xaccAccountCommitEdit (acc);
LEAVE ("acc=%s", acc->accountName);
}
/* ============================================================== */
@@ -407,111 +138,89 @@ void
xaccAccountScrubDoubleBalance (Account *acc)
{
LotList *node;
if (!acc) return;
ENTER ("acc=%s", acc->accountName);
for (node = acc->lots; node; node=node->next)
{
gnc_commodity *currency = NULL;
SplitList *snode;
gnc_numeric zero = gnc_numeric_zero();
gnc_numeric value = zero;
GNCLot *lot = node->data;
/* We examine only closed lots */
if (FALSE == gnc_lot_is_closed (lot)) continue;
for (snode = lot->splits; snode; snode=snode->next)
{
Split *s = snode->data;
Transaction *trans = s->parent;
/* Check to make sure all splits in the lot have a common currency */
if (NULL == currency)
{
currency = trans->common_currency;
}
if (FALSE == gnc_commodity_equiv (currency, trans->common_currency))
{
/* Unhandled error condition. We should do something
* graceful here. Don't know what. FIXME XXX */
PERR ("currencies in lot are not equivalent\n"
"\ttrans=%s curr=%s\n", xaccTransGetDescription(trans),
gnc_commodity_get_fullname(trans->common_currency));
}
/* Now, total up the values */
value = gnc_numeric_add (value, xaccSplitGetValue (s),
GNC_DENOM_AUTO, GNC_DENOM_LCD);
PINFO ("Split value=%s Accum Lot value=%s",
gnc_numeric_to_string (xaccSplitGetValue(s)),
gnc_numeric_to_string (value));
}
PINFO ("Lot value=%s", gnc_numeric_to_string (value));
/* Is the value of the lot zero? If not, add a balancing transaction.
* As per design doc lots.txt: the transaction has two splits,
* with equal & opposite values. The amt of one iz zero (so as
* not to upset the lot balance), the amt of the other is the same
* as its value (its the realized gain/loss).
*/
if (FALSE == gnc_numeric_equal (value, zero))
{
Transaction *trans;
Account *lot_acc, *gain_acc;
Split *lot_split, *gain_split;
Timespec ts;
lot_split = xaccMallocSplit (acc->book);
gain_split = xaccMallocSplit (acc->book);
gain_acc = GetOrMakeGainAcct (acc, currency);
xaccAccountBeginEdit (gain_acc);
xaccAccountInsertSplit (gain_acc, gain_split);
xaccAccountCommitEdit (gain_acc);
lot_acc = acc;
xaccAccountBeginEdit (lot_acc);
xaccAccountInsertSplit (lot_acc, lot_split);
xaccAccountCommitEdit (lot_acc);
trans = xaccMallocTransaction (acc->book);
xaccTransBeginEdit (trans);
xaccTransSetCurrency (trans, currency);
xaccTransSetDescription (trans, _("Realized Gain/Loss"));
ts = gnc_lot_get_close_date (lot);
xaccTransSetDatePostedTS (trans, &ts);
xaccTransSetDateEnteredSecs (trans, time(0));
xaccTransAppendSplit (trans, lot_split);
xaccTransAppendSplit (trans, gain_split);
xaccSplitSetMemo (lot_split, _("Realized Gain/Loss"));
xaccSplitSetAmount (lot_split, zero);
xaccSplitSetValue (lot_split, gnc_numeric_neg (value));
gnc_lot_add_split (lot, lot_split);
xaccSplitSetMemo (gain_split, _("Realized Gain/Loss"));
xaccSplitSetAmount (gain_split, value);
xaccSplitSetValue (gain_split, value);
xaccTransCommitEdit (trans);
}
xaccLotScrubDoubleBalance (lot);
}
LEAVE ("acc=%s", acc->accountName);
}
/* ============================================================== */
void
xaccLotScrubDoubleBalance (GNCLot *lot)
{
gnc_commodity *currency = NULL;
SplitList *snode;
gnc_numeric zero = gnc_numeric_zero();
gnc_numeric value = zero;
if (!lot) return;
ENTER ("lot=%s", kvp_frame_get_string (gnc_lot_get_slots (lot), "/title"));
for (snode = lot->splits; snode; snode=snode->next)
{
Split *s = snode->data;
xaccSplitComputeCapGains (s, NULL);
}
/* We double-check only closed lots */
if (FALSE == gnc_lot_is_closed (lot)) return;
for (snode = lot->splits; snode; snode=snode->next)
{
Split *s = snode->data;
Transaction *trans = s->parent;
/* Check to make sure all splits in the lot have a common currency */
if (NULL == currency)
{
currency = trans->common_currency;
}
if (FALSE == gnc_commodity_equiv (currency, trans->common_currency))
{
/* This lot has mixed currencies. Can't double-balance.
* Silently punt */
PWARN ("Lot with multiple currencies:\n"
"\ttrans=%s curr=%s\n", xaccTransGetDescription(trans),
gnc_commodity_get_fullname(trans->common_currency));
break;
}
/* Now, total up the values */
value = gnc_numeric_add_fixed (value, xaccSplitGetValue (s));
PINFO ("Split value=%s Accum Lot value=%s",
gnc_numeric_to_string (xaccSplitGetValue(s)),
gnc_numeric_to_string (value));
}
if (FALSE == gnc_numeric_equal (value, zero))
{
/* Unhandled error condition. Not sure what to do here,
* Since the ComputeCapGains should have gotten it right.
* I suppose there might be small rounding errors, a penny or two,
* the ideal thing would to figure out why there's a roudning
* error, and fix that.
*/
PERR ("Closed lot fails to double-balance !!\n");
}
LEAVE ("lot=%s", kvp_frame_get_string (gnc_lot_get_slots (lot), "/title"));
}
/* ============================================================== */
static gpointer
lot_scrub_cb (Account *acc, gpointer data)
{
if (FALSE == xaccAccountHasTrades (acc)) return NULL;
xaccAccountScrubLots (acc);
xaccAccountAssignLots (acc);
xaccAccountScrubDoubleBalance (acc);
return NULL;
}
@@ -528,7 +237,7 @@ xaccAccountScrubLotsBalance (Account *acc)
{
if (!acc) return;
if (FALSE == xaccAccountHasTrades (acc)) return;
xaccAccountScrubLots (acc);
xaccAccountAssignLots (acc);
xaccAccountScrubDoubleBalance (acc);
}
@@ -540,7 +249,7 @@ xaccAccountTreeScrubLotsBalance (Account *acc)
xaccGroupScrubLotsBalance (acc->children);
if (FALSE == xaccAccountHasTrades (acc)) return;
xaccAccountScrubLots (acc);
xaccAccountAssignLots (acc);
xaccAccountScrubDoubleBalance (acc);
}

View File

@@ -1,4 +1,6 @@
/********************************************************************\
* Scrub2.h -- Convert Stock Accounts to use Lots *
* *
* 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 *
@@ -36,57 +38,42 @@
#include "gnc-engine.h"
/** The xaccAccountHasTrades() method checks to see if the
* indicated account is used in the trading of commodities.
* A 'trading' account will contain transactions whose
* transaction currency is not the same as the account
* commodity. The existance of such transactions is
* the very definition of a 'trade'. This routine returns
* TRUE if this is a trading account, else it returns
* FALSE.
/** The xaccGroupScrubLotsBalance() routine walks the
* account tree, and invokes xaccAccountScrubLots()
* and xaccAccountScrubDoubleBalance() on all accounts
* that are trading accounts.
* The xaccAccountTreeScrubLotsBalance() does the same.
* The xaccAccountScrubLotsBalance() will do the same,
* except that it won't descend down to the account
* children.
*
* Most GUI routines will want to use one of these
* xacc[*]ScrubLotsBalance() routines, instead of the
* component ScrubLots() and ScrubDoubleBalance() routines,
* since it usually makes sense to call these together.
*/
gboolean xaccAccountHasTrades (Account *);
void xaccGroupScrubLotsBalance (AccountGroup *grp);
void xaccAccountScrubLotsBalance (Account *acc);
void xaccAccountTreeScrubLotsBalance (Account *acc);
/** The xaccAccountFindEarliestOpenLot() method is a handy
* utility routine for finding the earliest open lot in
* an account whose lot balance is *opposite* to the
* passed argument 'sign'. By 'earliest lot', we mean
* the lot that has a split with the earliest 'date_posted'.
* The sign comparison helps identify a lot that can be
* added to: usually, one wants to add splits to a lot so
* that the balance only decreases.
*/
GNCLot * xaccAccountFindEarliestOpenLot (Account *acc, gnc_numeric sign);
/** The xaccAccountGetDefaultGainAccount() routine will return
* the account to which realized gains/losses may be posted.
* Because gains may be in different currencies, one must
* specify the currency type in which the gains will be posted.
* This routine does nothing more than return the value of
* the "/lot-mgmt/gains-act/XXX" key, where XXX is the unique
* currency name. IOf there is no default account for this
* currency, NULL will be returned.
*/
Account * xaccAccountGetDefaultGainAccount (Account *acc, gnc_commodity * currency);
/** The xaccAccountSetDefaultGainAccount() routine can be used
* to set the account to which realized gains/losses will be
* posted by default. This routine does nothing more than set
* value of the "/lot-mgmt/gains-act/XXX" key, where XXX is the
* unique currency name of the currency of gains account.
*/
void xaccAccountSetDefaultGainAccount (Account *acc, Account *gains_acct);
/** The xaccAccountScrubLots() routine will walk over all of
/** The xaccAccountAssignLots() routine will walk over all of
* the splits in an account, and make sure that each belongs
* to a lot. Any splits that are not in a lot will be used
* to close the oldest open lot(s). If there are no open
* lots, a new lot will be started. By trying to close the
* oldest lots, this routine implements a FIFO acounting
* policy.
* to a lot. Currently, the default (and only implemented)
* assignment policy is a FIFO policy: Any splits that are
* not in a lot will be used to close the oldest open lot(s).
* If there are no open lots, a new lot will be started.
* By trying to close the oldest lots, this effectively
* implements a FIFO acounting policy.
*/
void xaccAccountScrubLots (Account *acc);
void xaccAccountAssignLots (Account *acc);
/** The xaccLotFill() routine attempts to assign splits to the
* indicated lot until the lot balance goes to zero, or until
* there are no suitable (i.e. unassigned) splits left in the
* account. It uses the default accounting policy to choose
* the splits to fill out the lot.
*/
void xaccLotFill (GNCLot *lot);
/** The xaccAccountScrubDoubleBalance() routine examines all
* of the closed lots in an account, and verifies that the
@@ -96,20 +83,27 @@ void xaccAccountScrubLots (Account *acc);
* found where the sum of the values is not zero, the lot
* is considered to have a 'realized gain or loss' that
* hadn't been correctly handled. This routine then creates
* a balancing transaction to make not of the realized
* gain/loss, adds it to the lot, and add it to a special
* orphaned gain/loss account.
* a balancing transaction so as to record the realized
* gain/loss, adds it to the lot, and adds it to a gain/loss
* account. If there is no default gain/loss account, it
* creates one.
*/
void xaccAccountScrubDoubleBalance (Account *acc);
/** The xaccGroupScrubLotsBalance() routine walks the
* account tree, and invokes xaccAccountScrubLots()
* and xaccAccountScrubDoubleBalance() on all accounts
* that are trading accounts.
/** The xaccLotScrubDoubleBalance() routine examines the indicated
* lot. If it is open, it does nothing. If it is closed,
* it then verifies that the lot is 'double balanced'.
* By 'double balance', we mean that both the sum of the
* split amounts is zero, and that the sum of the split
* values is zero. If the lot is closed and the sum of the
* values is not zero, the lot is considered to have a
* 'realized gain or loss' that hadn't been correctly handled.
* This routine then creates a balancing transaction to so
* as to record the realized gain/loss, adds it to the lot,
* and adds it to a gain/loss account. If there is no default
* gain/loss account, it creates one.
*/
void xaccGroupScrubLotsBalance (AccountGroup *grp);
void xaccAccountScrubLotsBalance (Account *acc);
void xaccAccountTreeScrubLotsBalance (Account *acc);
void xaccLotScrubDoubleBalance (GNCLot *lot);
#endif /* XACC_SCRUB2_H */
/** @} */

View File

@@ -141,11 +141,11 @@ xaccOpenLog (void)
g_free (timestamp);
/* use tab-separated fields */
fprintf (trans_log, "mod trans_guid split_guid time_now " \
"date_entered date_posted " \
"acc_guid acc_name num description " \
"notes memo action reconciled " \
"amount value date_reconciled\n");
fprintf (trans_log, "mod trans_guid split_guid time_now " \
"date_entered date_posted " \
"acc_guid acc_name num description " \
"notes memo action reconciled " \
"amount value date_reconciled\n");
fprintf (trans_log, "-----------------\n");
}
@@ -190,23 +190,31 @@ xaccTransWriteLog (Transaction *trans, char flag)
trans_notes = xaccTransGetNotes(trans);
fprintf (trans_log, "===== START\n");
for (node = trans->splits; node; node = node->next) {
for (node = trans->splits; node; node = node->next)
{
Split *split = node->data;
const char * accname = "";
char acc_guid_str[GUID_ENCODING_LENGTH+1];
gnc_numeric amt,val;
if (xaccSplitGetAccount(split)){
if (xaccSplitGetAccount(split))
{
accname = xaccAccountGetName (xaccSplitGetAccount(split));
guid_to_string_buff(xaccAccountGetGUID(xaccSplitGetAccount(split)),
acc_guid_str);
} else {
acc_guid_str[0] = '\0';
guid_to_string_buff(xaccAccountGetGUID(xaccSplitGetAccount(split)),
acc_guid_str);
}
else
{
acc_guid_str[0] = '\0';
}
timespecFromTime_t(&ts,split->date_reconciled.tv_sec);
gnc_timespec_to_iso8601_buff (ts, drecn);
timespecFromTime_t(&ts,split->date_reconciled.tv_sec);
gnc_timespec_to_iso8601_buff (ts, drecn);
guid_to_string_buff (xaccSplitGetGUID(split), split_guid_str);
amt = xaccSplitGetAmount (split);
val = xaccSplitGetValue (split);
/* use tab-separated fields */
fprintf (trans_log,
"%c\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t"
@@ -216,7 +224,7 @@ xaccTransWriteLog (Transaction *trans, char flag)
dnow ? dnow : "",
dent ? dent : "",
dpost ? dpost : "",
acc_guid_str,
acc_guid_str,
accname ? accname : "",
trans->num ? trans->num : "",
trans->description ? trans->description : "",
@@ -224,10 +232,10 @@ xaccTransWriteLog (Transaction *trans, char flag)
split->memo ? split->memo : "",
split->action ? split->action : "",
split->reconciled,
(long long int) gnc_numeric_num(split->amount),
(long long int) gnc_numeric_denom(split->amount),
(long long int) gnc_numeric_num(split->value),
(long long int) gnc_numeric_denom(split->value),
(long long int) gnc_numeric_num(amt),
(long long int) gnc_numeric_denom(amt),
(long long int) gnc_numeric_num(val),
(long long int) gnc_numeric_denom(val),
drecn ? drecn : "");
}
@@ -248,12 +256,13 @@ xaccTransWriteLog (Transaction *trans, char flag)
*/
char *
xaccSplitAsString(Split *split, const char prefix[]) {
xaccSplitAsString(Split *split, const char prefix[])
{
char *result = NULL;
size_t result_size;
FILE *stream = open_memstream(&result, &result_size);
const char *split_memo = xaccSplitGetMemo(split);
const double split_value = DxaccSplitGetValue(split);
const double split_value = gnc_numeric_to_double(xaccSplitGetValue(split));
Account *split_dest = xaccSplitGetAccount(split);
const char *dest_name =
split_dest ? xaccAccountGetName(split_dest) : NULL;
@@ -287,7 +296,8 @@ xaccTransGetDateStr (Transaction *trans)
}
char *
xaccTransAsString(Transaction *txn, const char prefix[]) {
xaccTransAsString(Transaction *txn, const char prefix[])
{
char *result = NULL;
size_t result_size;
FILE *stream = open_memstream(&result, &result_size);
@@ -295,7 +305,7 @@ xaccTransAsString(Transaction *txn, const char prefix[]) {
const char *num = xaccTransGetNum(txn);
const char *desc = xaccTransGetDescription(txn);
const char *memo = xaccSplitGetMemo(xaccTransGetSplit(txn, 0));
const double total = DxaccSplitGetValue(xaccTransGetSplit(txn, 0));
const double total = gnc_numeric_to_double(xaccSplitGetValue(xaccTransGetSplit(txn, 0)));
g_return_val_if_fail (stream, NULL);

File diff suppressed because it is too large Load Diff

View File

@@ -57,39 +57,6 @@
#define TXN_TYPE_PAYMENT 'P' /**< Transaction is a payment */
/**@}*/
/** @name Configuration ForceDoubleEntry getters/setters
*/
/**@{*/
/**
* The xaccConfigSetForceDoubleEntry() and xaccConfigGetForceDoubleEntry()
* set and get the "force_double_entry" flag. This flag determines how
* the splits in a transaction will be balanced.
*
* The following values have significance:
*
* 0 -- anything goes
*
* 1 -- The sum of all splits in a transaction will be
* forced to be zero, even if this requires the
* creation of additional splits. Note that a split
* whose value is zero (e.g. a stock price) can exist
* by itself. Otherwise, all splits must come in at
* least pairs.
*
* 2 -- splits without parents will be forced into a
* lost & found account. (Not implemented)
*/
void xaccConfigSetForceDoubleEntry (int force);
/**
* The xaccConfigSetForceDoubleEntry() and xaccConfigGetForceDoubleEntry()
* set and get the "force_double_entry" flag. This flag determines how
* the splits in a transaction will be balanced.
*/
int xaccConfigGetForceDoubleEntry (void);
/**@}*/
/***************************************************************
* Transaction
*/
@@ -362,18 +329,15 @@ int xaccTransOrder (const Transaction *ta, const Transaction *tb);
void xaccTransSetDate (Transaction *trans,
int day, int mon, int year);
/** The xaccTransSetDateSecs() method will modify the <i>posted</i>
/** The xaccTransSetDatePostedSecs() method will modify the <i>posted</i>
date of the transaction, specified by a time_t (see ctime(3)). The
posted date is the date when this transaction was posted at the
bank. */
void xaccTransSetDateSecs (Transaction *trans, time_t time);
/** xaccTransSetDatePostedSecs() is just an alias for
xaccTransSetDateSecs() -- both functions access the same date. */
#define xaccTransSetDateSecs xaccTransSetDatePostedSecs
void xaccTransSetDatePostedSecs (Transaction *trans, time_t time);
/** The xaccTransSetDatePostedTS() method does the same thing as
xaccTransSetDate[Posted]Secs(), but takes a struct timespec64. */
xaccTransSetDatePostedSecs(), but takes a struct timespec64. */
void xaccTransSetDatePostedTS (Transaction *trans,
const Timespec *ts);
@@ -541,9 +505,8 @@ Timespec xaccSplitRetDateReconciledTS (const Split *split);
*/
/*@{*/
/** The xaccSplitSetAmount() (formerly xaccSplitSetShareAmount) method
* sets the amount in the account's commodity that the split should
* have.
/** The xaccSplitSetAmount() method sets the amount in the account's
* commodity that the split should have.
*
* The following four setter functions set the prices and amounts.
* All of the routines always maintain balance: that is, invoking any
@@ -562,7 +525,10 @@ Timespec xaccSplitRetDateReconciledTS (const Split *split);
*/
void xaccSplitSetAmount (Split *split, gnc_numeric amount);
/** Returns the amount of the split in the account's commodity. */
/** Returns the amount of the split in the account's commodity.
* Note that for cap-gains splits, this is slaved to the transaction
* that is causing the gains to occur.
*/
gnc_numeric xaccSplitGetAmount (const Split * split);
/** The xaccSplitSetValue() method sets the value of this split in the
@@ -573,7 +539,10 @@ gnc_numeric xaccSplitGetAmount (const Split * split);
*/
void xaccSplitSetValue (Split *split, gnc_numeric value);
/** Returns the value of this split in the transaction's commodity. */
/** Returns the value of this split in the transaction's commodity.
* Note that for cap-gains splits, this is slaved to the transaction
* that is causing the gains to occur.
*/
gnc_numeric xaccSplitGetValue (const Split * split);
/** The xaccSplitSetSharePriceAndAmount() method will simultaneously
@@ -592,13 +561,17 @@ gnc_numeric xaccSplitGetSharePrice (const Split * split);
/** Depending on the base_currency, set either the value or the amount
* of this split or both: If the base_currency is the transaction's
* commodity, set the value. If it's the account's commodity, set the
* commodity, set the value. If it is the account's commodity, set the
* amount. If both, set both.
*
* @note This function is useful when filling in the value/amount for
* a newly created transaction, since otherwise you have to manually
* make sure that both Value and Amount are correctly set (and not
* that value or amount remains zero). */
* @note <b>WATCH OUT:</b> When using this function and the
* transaction's and account's commodities are different, the amount
* or the value will be left as zero. This might screw up the
* multi-currency handling code in the register. So please think twice
* whether you need this function -- using xaccSplitSetValue()
* together with xaccSplitSetAmount() is definitely the better and
* safer solution!
*/
void xaccSplitSetBaseValue (Split *split, gnc_numeric value,
const gnc_commodity * base_currency);
@@ -765,36 +738,9 @@ const char * xaccSplitGetCorrAccountCode(const Split *sa);
* split. DEPRECATED - set the value and amount instead. */
void xaccSplitSetSharePrice (Split *split, gnc_numeric price);
/** @deprecated Don't use doubles anymore, only use gnc_numerics. */
void DxaccSplitSetAmount (Split *s, double damt);
/** @deprecated Don't use doubles anymore, only use gnc_numerics.
*
* WARNING: The xaccSplitSetValue and DxaccSplitSetValue do NOT have the same
* behavior. The later divides the value given by the current value and set's
* the result as the new split value. Is that a but or just strange undocumented
* feature? Benoit Gr<47>goire 2002-6-12 */
void DxaccSplitSetValue (Split *split, double value);
/** @deprecated Don't use doubles anymore, only use gnc_numerics. */
double DxaccSplitGetValue (const Split * split);
/** @deprecated Don't use doubles anymore, only use gnc_numerics. */
void DxaccSplitSetSharePriceAndAmount (Split *split, double price,
double amount);
/** @deprecated Don't use doubles anymore, only use gnc_numerics. */
void DxaccSplitSetShareAmount (Split *split, double amount);
/** @deprecated Don't use doubles anymore, only use gnc_numerics. */
double DxaccSplitGetShareAmount (const Split * split);
/** @deprecated Don't use doubles anymore, only use gnc_numerics. */
void DxaccSplitSetSharePrice (Split *split, double price);
/** @deprecated Don't use doubles anymore, only use gnc_numerics. */
double DxaccSplitGetSharePrice (const Split * split);
/** @deprecated Don't use doubles anymore, only use gnc_numerics. */
void DxaccSplitSetBaseValue (Split *split, double value,
const gnc_commodity * base_currency);
/*@}*/
/********************************************************************\
* Miscellaneous utility routines.
\********************************************************************/

View File

@@ -76,6 +76,17 @@
* A "split" is more commonly refered to as a "entry" in a "transaction".
*/
/* Flags for handling cap-gains status */
#define GAINS_STATUS_UNKNOWN 0xff
#define GAINS_STATUS_CLEAN 0x0
#define GAINS_STATUS_GAINS 0x3
#define GAINS_STATUS_DATE_DIRTY 0x10
#define GAINS_STATUS_AMNT_DIRTY 0x20
#define GAINS_STATUS_VALU_DIRTY 0x40
#define GAINS_STATUS_LOT_DIRTY 0x80
#define GAINS_STATUS_ADIRTY (GAINS_STATUS_AMNT_DIRTY|GAINS_STATUS_LOT_DIRTY)
#define GAINS_STATUS_VDIRTY (GAINS_STATUS_VALU_DIRTY)
#define GAINS_STATUS_A_VDIRTY (GAINS_STATUS_AMNT_DIRTY|GAINS_STATUS_VALU_DIRTY|GAINS_STATUS_LOT_DIRTY)
struct split_s
{
@@ -108,10 +119,23 @@ struct split_s
* it's NULL until accessed. */
KvpFrame * kvp_data;
char reconciled; /* The reconciled field */
Timespec date_reconciled; /* date split was reconciled */
char reconciled; /* The reconciled field */
/* 'value' is the amount of the transaction balancing commodity
/* gains is a flag used to track the relationship between
* capital-gains splits. Depending on its value, this flag indicates
* if this split is the source of gains, if this split is a record
* of the gains, and if values are 'dirty' and need to be recomputed.
*/
unsigned char gains;
/* 'gains_split' is a convenience pointer used to track down the
* other end of a cap-gains transaction pair. NULL if this split
* doesn't involve cap gains.
*/
Split *gains_split;
/* 'value' is the quantity of the transaction balancing commodity
* (i.e. currency) involved, 'amount' is the amount of the account's
* commodity involved. */
gnc_numeric value;
@@ -226,7 +250,7 @@ void xaccFreeSplit (Split *split); /* frees memory */
*/
Transaction * xaccDupeTransaction (Transaction *t);
/* compute the value of a list of splits in the given currency,
/* Compute the value of a list of splits in the given currency,
* excluding the skip_me split. */
gnc_numeric xaccSplitsComputeValue (GList *splits, Split * skip_me,
const gnc_commodity * base_currency);
@@ -239,13 +263,6 @@ gnc_numeric xaccSplitsComputeValue (GList *splits, Split * skip_me,
void xaccTransSetVersion (Transaction*, gint32);
gint32 xaccTransGetVersion (const Transaction*);
/* The xaccTransFindCommonCurrency () method returns a gnc_commodity
* indicating a currency denomination that all of the splits in this
* transaction have in common, using the old currency/security fields
* of the split accounts. */
gnc_commodity * xaccTransFindOldCommonCurrency (Transaction *trans,
QofBook *book);
/* Code to register Split and Transaction types with the engine */
gboolean xaccSplitRegister (void);
gboolean xaccTransRegister (void);
@@ -257,4 +274,22 @@ gboolean xaccTransRegister (void);
*/
QofBackend * xaccTransactionGetBackend (Transaction *trans);
/* The xaccSplitDetermineGainStatus() routine will analyze the
* the split, and try to set the internal status flags
* appropriately for the split. These flags indicate if the split
* represents cap gains, and if the gains value/amount needs to be
* recomputed.
*/
void xaccSplitDetermineGainStatus (Split *split);
/* ---------------------------------------------------------------- */
/* Depricated routines */
void DxaccSplitSetSharePriceAndAmount (Split *split,
double price,
double amount);
void DxaccSplitSetShareAmount (Split *split, double amount);
/*@}*/
#endif /* XACC_TRANSACTION_P_H */

View File

@@ -36,6 +36,7 @@
#include "gnc-engine-util.h"
#include "gnc-engine.h"
#include "gnc-numeric.h"
#include "gnc-trace.h"
#include "guile-mappings.h"
#include "qofbook.h"
#include "qofquery.h"
@@ -1706,6 +1707,8 @@ gnc_query2scm (QofQuery *q)
if (!q) return SCM_BOOL_F;
++scm_block_gc;
/* terms */
pair = scm_cons (gnc_query_terms2scm (qof_query_get_terms (q)), SCM_EOL);
pair = scm_cons (scm_str2symbol ("terms"), pair);
@@ -1741,6 +1744,7 @@ gnc_query2scm (QofQuery *q)
/* Reverse this list; tag it as 'query-v2' */
pair = scm_reverse (query_scm);
--scm_block_gc;
return scm_cons (scm_str2symbol ("query-v2"), pair);
}
@@ -1964,6 +1968,8 @@ gnc_scm2query_v2 (SCM query_scm)
gboolean si1 = TRUE, si2 = TRUE, si3 = TRUE;
int max_results = -1;
++scm_block_gc;
while (!SCM_NULLP (query_scm))
{
char *symbol;
@@ -2051,6 +2057,8 @@ gnc_scm2query_v2 (SCM query_scm)
free (symbol);
}
--scm_block_gc;
if (ok && search_for) {
qof_query_search_for (q, search_for);
qof_query_set_sort_order (q, sp1, sp2, sp3);

Some files were not shown because too many files have changed in this diff Show More