mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
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:
@@ -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 = \
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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ö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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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ö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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
40
rpm/README
40
rpm/README
@@ -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.
|
||||
|
||||
|
||||
@@ -45,4 +45,5 @@ bin_SCRIPTS = gnc-test-env
|
||||
EXTRA_DIST = \
|
||||
.cvsignore \
|
||||
README.modules \
|
||||
gnc-test-env
|
||||
gnc-test-env \
|
||||
valgrind-gnucash.supp
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
SUBDIRS = gnome . test
|
||||
PWD := $(shell pwd)
|
||||
|
||||
pkglib_LTLIBRARIES = libgncmod-app-file.la libgw-app-file.la
|
||||
|
||||
|
||||
@@ -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 -------------- */
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
SUBDIRS = . test
|
||||
PWD := $(shell pwd)
|
||||
|
||||
pkglib_LTLIBRARIES = libgncmod-app-utils.la libgw-app-utils.la
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ();
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 ===================== */
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 *
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ guile_main (void *closure, int argc, char **argv)
|
||||
|
||||
if (!location)
|
||||
{
|
||||
location = "../../../../accounts/C";
|
||||
location = "../../../../accounts/C";
|
||||
}
|
||||
|
||||
gnc_module_system_init();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -53,7 +53,8 @@ noinst_HEADERS = \
|
||||
putil.h \
|
||||
txn.h \
|
||||
txnmass.h \
|
||||
upgrade.h
|
||||
upgrade.h \
|
||||
newtables.h
|
||||
|
||||
EXTRA_DIST = \
|
||||
.cvsignore \
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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, "';");
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
SUBDIRS = . test file
|
||||
PWD := $(shell pwd)
|
||||
|
||||
pkglib_LTLIBRARIES = libgw-business-core.la libgncmod-business-core.la
|
||||
|
||||
|
||||
@@ -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 }
|
||||
};
|
||||
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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 }
|
||||
};
|
||||
|
||||
|
||||
@@ -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 }
|
||||
};
|
||||
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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 }
|
||||
};
|
||||
|
||||
|
||||
@@ -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 }
|
||||
};
|
||||
|
||||
|
||||
@@ -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 }
|
||||
};
|
||||
|
||||
|
||||
@@ -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 }
|
||||
};
|
||||
|
||||
|
||||
@@ -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 }
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
SUBDIRS = . glade ui
|
||||
PWD := $(shell pwd)
|
||||
|
||||
pkglib_LTLIBRARIES = libgncmod-business-gnome.la libgw-business-gnome.la
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
PWD := $(shell pwd)
|
||||
|
||||
pkglib_LTLIBRARIES = libgncmod-dialog-tax-table.la libgw-dialog-tax-table.la
|
||||
|
||||
AM_CFLAGS = \
|
||||
|
||||
@@ -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 ========================
|
||||
|
||||
|
||||
104
src/doc/lots.txt
104
src/doc/lots.txt
@@ -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 ------------------------------
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/********************************************************************\
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/********************************************************************\
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
/** @} */
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 */
|
||||
/** @} */
|
||||
|
||||
@@ -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
@@ -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.
|
||||
\********************************************************************/
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user