Revised how commodities are dealt with.
This commit is contained in:
parent
f9f24fab93
commit
c59018c29d
31 changed files with 1146 additions and 915 deletions
|
|
@ -172,7 +172,7 @@ ledger_so_SOURCES = src/pyledger.cc
|
||||||
ledger_so_DEPENDENCIES = libledger.la gdtoa/libgdtoa.la libpyledger.la
|
ledger_so_DEPENDENCIES = libledger.la gdtoa/libgdtoa.la libpyledger.la
|
||||||
|
|
||||||
PYLIBS = pyledger ledger gdtoa gmp boost_date_time \
|
PYLIBS = pyledger ledger gdtoa gmp boost_date_time \
|
||||||
boost_filesystem boost_regex boost_python
|
boost_signals boost_filesystem boost_regex boost_python
|
||||||
|
|
||||||
if HAVE_EXPAT
|
if HAVE_EXPAT
|
||||||
PYLIBS += expat
|
PYLIBS += expat
|
||||||
|
|
|
||||||
|
|
@ -427,10 +427,10 @@ dist_lisp_LISP = lisp/ledger.el lisp/timeclock.el
|
||||||
@HAVE_BOOST_PYTHON_TRUE@ledger_so_SOURCES = src/pyledger.cc
|
@HAVE_BOOST_PYTHON_TRUE@ledger_so_SOURCES = src/pyledger.cc
|
||||||
@HAVE_BOOST_PYTHON_TRUE@ledger_so_DEPENDENCIES = libledger.la gdtoa/libgdtoa.la libpyledger.la
|
@HAVE_BOOST_PYTHON_TRUE@ledger_so_DEPENDENCIES = libledger.la gdtoa/libgdtoa.la libpyledger.la
|
||||||
@HAVE_BOOST_PYTHON_TRUE@PYLIBS = pyledger ledger gdtoa gmp \
|
@HAVE_BOOST_PYTHON_TRUE@PYLIBS = pyledger ledger gdtoa gmp \
|
||||||
@HAVE_BOOST_PYTHON_TRUE@ boost_date_time boost_filesystem \
|
@HAVE_BOOST_PYTHON_TRUE@ boost_date_time boost_signals \
|
||||||
@HAVE_BOOST_PYTHON_TRUE@ boost_regex boost_python \
|
@HAVE_BOOST_PYTHON_TRUE@ boost_filesystem boost_regex \
|
||||||
@HAVE_BOOST_PYTHON_TRUE@ $(am__append_15) $(am__append_16) \
|
@HAVE_BOOST_PYTHON_TRUE@ boost_python $(am__append_15) \
|
||||||
@HAVE_BOOST_PYTHON_TRUE@ $(am__append_17)
|
@HAVE_BOOST_PYTHON_TRUE@ $(am__append_16) $(am__append_17)
|
||||||
nodist_UnitTests_SOURCES = tests/UnitTests.cc \
|
nodist_UnitTests_SOURCES = tests/UnitTests.cc \
|
||||||
\
|
\
|
||||||
tests/numerics/BasicAmount.cc \
|
tests/numerics/BasicAmount.cc \
|
||||||
|
|
|
||||||
98
configure
vendored
98
configure
vendored
|
|
@ -1,6 +1,6 @@
|
||||||
#! /bin/sh
|
#! /bin/sh
|
||||||
# Guess values for system-dependent variables and create Makefiles.
|
# Guess values for system-dependent variables and create Makefiles.
|
||||||
# Generated by GNU Autoconf 2.61 for ledger 3.0-git-lexical_cast.
|
# Generated by GNU Autoconf 2.61 for ledger 3.0-git-commodity_pool.
|
||||||
#
|
#
|
||||||
# Report bugs to <johnw@newartisans.com>.
|
# Report bugs to <johnw@newartisans.com>.
|
||||||
#
|
#
|
||||||
|
|
@ -728,8 +728,8 @@ SHELL=${CONFIG_SHELL-/bin/sh}
|
||||||
# Identity of this package.
|
# Identity of this package.
|
||||||
PACKAGE_NAME='ledger'
|
PACKAGE_NAME='ledger'
|
||||||
PACKAGE_TARNAME='ledger'
|
PACKAGE_TARNAME='ledger'
|
||||||
PACKAGE_VERSION='3.0-git-lexical_cast'
|
PACKAGE_VERSION='3.0-git-commodity_pool'
|
||||||
PACKAGE_STRING='ledger 3.0-git-lexical_cast'
|
PACKAGE_STRING='ledger 3.0-git-commodity_pool'
|
||||||
PACKAGE_BUGREPORT='johnw@newartisans.com'
|
PACKAGE_BUGREPORT='johnw@newartisans.com'
|
||||||
|
|
||||||
ac_unique_file="ledger"
|
ac_unique_file="ledger"
|
||||||
|
|
@ -1424,7 +1424,7 @@ if test "$ac_init_help" = "long"; then
|
||||||
# Omit some internal or obsolete options to make the list less imposing.
|
# Omit some internal or obsolete options to make the list less imposing.
|
||||||
# This message is too long to be a string in the A/UX 3.1 sh.
|
# This message is too long to be a string in the A/UX 3.1 sh.
|
||||||
cat <<_ACEOF
|
cat <<_ACEOF
|
||||||
\`configure' configures ledger 3.0-git-lexical_cast to adapt to many kinds of systems.
|
\`configure' configures ledger 3.0-git-commodity_pool to adapt to many kinds of systems.
|
||||||
|
|
||||||
Usage: $0 [OPTION]... [VAR=VALUE]...
|
Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||||
|
|
||||||
|
|
@ -1494,7 +1494,7 @@ fi
|
||||||
|
|
||||||
if test -n "$ac_init_help"; then
|
if test -n "$ac_init_help"; then
|
||||||
case $ac_init_help in
|
case $ac_init_help in
|
||||||
short | recursive ) echo "Configuration of ledger 3.0-git-lexical_cast:";;
|
short | recursive ) echo "Configuration of ledger 3.0-git-commodity_pool:";;
|
||||||
esac
|
esac
|
||||||
cat <<\_ACEOF
|
cat <<\_ACEOF
|
||||||
|
|
||||||
|
|
@ -1605,7 +1605,7 @@ fi
|
||||||
test -n "$ac_init_help" && exit $ac_status
|
test -n "$ac_init_help" && exit $ac_status
|
||||||
if $ac_init_version; then
|
if $ac_init_version; then
|
||||||
cat <<\_ACEOF
|
cat <<\_ACEOF
|
||||||
ledger configure 3.0-git-lexical_cast
|
ledger configure 3.0-git-commodity_pool
|
||||||
generated by GNU Autoconf 2.61
|
generated by GNU Autoconf 2.61
|
||||||
|
|
||||||
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
|
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
|
||||||
|
|
@ -1619,7 +1619,7 @@ cat >config.log <<_ACEOF
|
||||||
This file contains any messages produced by compilers while
|
This file contains any messages produced by compilers while
|
||||||
running configure, to aid debugging if configure makes a mistake.
|
running configure, to aid debugging if configure makes a mistake.
|
||||||
|
|
||||||
It was created by ledger $as_me 3.0-git-lexical_cast, which was
|
It was created by ledger $as_me 3.0-git-commodity_pool, which was
|
||||||
generated by GNU Autoconf 2.61. Invocation command line was
|
generated by GNU Autoconf 2.61. Invocation command line was
|
||||||
|
|
||||||
$ $0 $@
|
$ $0 $@
|
||||||
|
|
@ -2310,7 +2310,7 @@ fi
|
||||||
|
|
||||||
# Define the identity of the package.
|
# Define the identity of the package.
|
||||||
PACKAGE='ledger'
|
PACKAGE='ledger'
|
||||||
VERSION='3.0-git-lexical_cast'
|
VERSION='3.0-git-commodity_pool'
|
||||||
|
|
||||||
|
|
||||||
cat >>confdefs.h <<_ACEOF
|
cat >>confdefs.h <<_ACEOF
|
||||||
|
|
@ -19691,6 +19691,84 @@ See \`config.log' for more details." >&2;}
|
||||||
{ (exit 1); exit 1; }; }
|
{ (exit 1); exit 1; }; }
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# check for boost_signals
|
||||||
|
{ echo "$as_me:$LINENO: checking if boost_signals is available" >&5
|
||||||
|
echo $ECHO_N "checking if boost_signals is available... $ECHO_C" >&6; }
|
||||||
|
if test "${boost_signals_cpplib_avail+set}" = set; then
|
||||||
|
echo $ECHO_N "(cached) $ECHO_C" >&6
|
||||||
|
else
|
||||||
|
boost_signals_save_libs=$LIBS
|
||||||
|
LIBS="-lboost_signals $LIBS"
|
||||||
|
ac_ext=cpp
|
||||||
|
ac_cpp='$CXXCPP $CPPFLAGS'
|
||||||
|
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
|
||||||
|
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
|
||||||
|
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
|
||||||
|
|
||||||
|
cat >conftest.$ac_ext <<_ACEOF
|
||||||
|
/* confdefs.h. */
|
||||||
|
_ACEOF
|
||||||
|
cat confdefs.h >>conftest.$ac_ext
|
||||||
|
cat >>conftest.$ac_ext <<_ACEOF
|
||||||
|
/* end confdefs.h. */
|
||||||
|
#include <boost/signal.hpp>
|
||||||
|
int
|
||||||
|
main ()
|
||||||
|
{
|
||||||
|
boost::signal<void (void)> this_signal;
|
||||||
|
;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
_ACEOF
|
||||||
|
rm -f conftest.$ac_objext conftest$ac_exeext
|
||||||
|
if { (ac_try="$ac_link"
|
||||||
|
case "(($ac_try" in
|
||||||
|
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
|
||||||
|
*) ac_try_echo=$ac_try;;
|
||||||
|
esac
|
||||||
|
eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
|
||||||
|
(eval "$ac_link") 2>conftest.er1
|
||||||
|
ac_status=$?
|
||||||
|
grep -v '^ *+' conftest.er1 >conftest.err
|
||||||
|
rm -f conftest.er1
|
||||||
|
cat conftest.err >&5
|
||||||
|
echo "$as_me:$LINENO: \$? = $ac_status" >&5
|
||||||
|
(exit $ac_status); } && {
|
||||||
|
test -z "$ac_cxx_werror_flag" ||
|
||||||
|
test ! -s conftest.err
|
||||||
|
} && test -s conftest$ac_exeext &&
|
||||||
|
$as_test_x conftest$ac_exeext; then
|
||||||
|
boost_signals_cpplib_avail=true
|
||||||
|
else
|
||||||
|
echo "$as_me: failed program was:" >&5
|
||||||
|
sed 's/^/| /' conftest.$ac_ext >&5
|
||||||
|
|
||||||
|
boost_signals_cpplib_avail=false
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
|
||||||
|
conftest$ac_exeext conftest.$ac_ext
|
||||||
|
ac_ext=c
|
||||||
|
ac_cpp='$CPP $CPPFLAGS'
|
||||||
|
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
|
||||||
|
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
|
||||||
|
ac_compiler_gnu=$ac_cv_c_compiler_gnu
|
||||||
|
|
||||||
|
LIBS=$boost_signals_save_libs
|
||||||
|
fi
|
||||||
|
{ echo "$as_me:$LINENO: result: $boost_signals_cpplib_avail" >&5
|
||||||
|
echo "${ECHO_T}$boost_signals_cpplib_avail" >&6; }
|
||||||
|
|
||||||
|
if test x$boost_signals_cpplib_avail = xtrue ; then
|
||||||
|
LIBS="-lboost_signals $LIBS"
|
||||||
|
else
|
||||||
|
{ { echo "$as_me:$LINENO: error: \"Could not find boost_signals library (set CPPFLAGS and LDFLAGS?)\"
|
||||||
|
See \`config.log' for more details." >&5
|
||||||
|
echo "$as_me: error: \"Could not find boost_signals library (set CPPFLAGS and LDFLAGS?)\"
|
||||||
|
See \`config.log' for more details." >&2;}
|
||||||
|
{ (exit 1); exit 1; }; }
|
||||||
|
fi
|
||||||
|
|
||||||
# check for gmp
|
# check for gmp
|
||||||
{ echo "$as_me:$LINENO: checking if libgmp is available" >&5
|
{ echo "$as_me:$LINENO: checking if libgmp is available" >&5
|
||||||
echo $ECHO_N "checking if libgmp is available... $ECHO_C" >&6; }
|
echo $ECHO_N "checking if libgmp is available... $ECHO_C" >&6; }
|
||||||
|
|
@ -21967,7 +22045,7 @@ exec 6>&1
|
||||||
# report actual input values of CONFIG_FILES etc. instead of their
|
# report actual input values of CONFIG_FILES etc. instead of their
|
||||||
# values after options handling.
|
# values after options handling.
|
||||||
ac_log="
|
ac_log="
|
||||||
This file was extended by ledger $as_me 3.0-git-lexical_cast, which was
|
This file was extended by ledger $as_me 3.0-git-commodity_pool, which was
|
||||||
generated by GNU Autoconf 2.61. Invocation command line was
|
generated by GNU Autoconf 2.61. Invocation command line was
|
||||||
|
|
||||||
CONFIG_FILES = $CONFIG_FILES
|
CONFIG_FILES = $CONFIG_FILES
|
||||||
|
|
@ -22020,7 +22098,7 @@ Report bugs to <bug-autoconf@gnu.org>."
|
||||||
_ACEOF
|
_ACEOF
|
||||||
cat >>$CONFIG_STATUS <<_ACEOF
|
cat >>$CONFIG_STATUS <<_ACEOF
|
||||||
ac_cs_version="\\
|
ac_cs_version="\\
|
||||||
ledger config.status 3.0-git-lexical_cast
|
ledger config.status 3.0-git-commodity_pool
|
||||||
configured by $0, generated by GNU Autoconf 2.61,
|
configured by $0, generated by GNU Autoconf 2.61,
|
||||||
with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"
|
with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"
|
||||||
|
|
||||||
|
|
|
||||||
23
configure.in
23
configure.in
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
AC_PREREQ(2.59)
|
AC_PREREQ(2.59)
|
||||||
|
|
||||||
AC_INIT(ledger, 3.0-git-lexical_cast, johnw@newartisans.com)
|
AC_INIT(ledger, 3.0-git-commodity_pool, johnw@newartisans.com)
|
||||||
AC_CONFIG_SRCDIR(ledger)
|
AC_CONFIG_SRCDIR(ledger)
|
||||||
AM_INIT_AUTOMAKE
|
AM_INIT_AUTOMAKE
|
||||||
|
|
||||||
|
|
@ -148,6 +148,27 @@ else
|
||||||
AC_MSG_FAILURE("Could not find boost_filesystem library (set CPPFLAGS and LDFLAGS?)")
|
AC_MSG_FAILURE("Could not find boost_filesystem library (set CPPFLAGS and LDFLAGS?)")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# check for boost_signals
|
||||||
|
AC_CACHE_CHECK(
|
||||||
|
[if boost_signals is available],
|
||||||
|
[boost_signals_cpplib_avail],
|
||||||
|
[boost_signals_save_libs=$LIBS
|
||||||
|
LIBS="-lboost_signals $LIBS"
|
||||||
|
AC_LANG_PUSH(C++)
|
||||||
|
AC_TRY_LINK(
|
||||||
|
[#include <boost/signal.hpp>],
|
||||||
|
[boost::signal<void (void)> this_signal;],
|
||||||
|
[boost_signals_cpplib_avail=true],
|
||||||
|
[boost_signals_cpplib_avail=false])
|
||||||
|
AC_LANG_POP
|
||||||
|
LIBS=$boost_signals_save_libs])
|
||||||
|
|
||||||
|
if [test x$boost_signals_cpplib_avail = xtrue ]; then
|
||||||
|
LIBS="-lboost_signals $LIBS"
|
||||||
|
else
|
||||||
|
AC_MSG_FAILURE("Could not find boost_signals library (set CPPFLAGS and LDFLAGS?)")
|
||||||
|
fi
|
||||||
|
|
||||||
# check for gmp
|
# check for gmp
|
||||||
AC_CACHE_CHECK(
|
AC_CACHE_CHECK(
|
||||||
[if libgmp is available],
|
[if libgmp is available],
|
||||||
|
|
|
||||||
256
src/amount.cc
256
src/amount.cc
|
|
@ -46,6 +46,8 @@
|
||||||
|
|
||||||
namespace ledger {
|
namespace ledger {
|
||||||
|
|
||||||
|
commodity_pool_t * amount_t::default_pool = NULL;
|
||||||
|
|
||||||
bool amount_t::keep_base = false;
|
bool amount_t::keep_base = false;
|
||||||
|
|
||||||
bool amount_t::keep_price = false;
|
bool amount_t::keep_price = false;
|
||||||
|
|
@ -109,25 +111,12 @@ void amount_t::initialize()
|
||||||
mpz_init(temp);
|
mpz_init(temp);
|
||||||
mpz_init(divisor);
|
mpz_init(divisor);
|
||||||
|
|
||||||
|
// jww (2007-05-02): Be very careful here!
|
||||||
|
if (! default_pool)
|
||||||
|
default_pool = new commodity_pool_t;
|
||||||
|
|
||||||
true_value = new amount_t::bigint_t;
|
true_value = new amount_t::bigint_t;
|
||||||
mpz_set_ui(true_value->val, 1);
|
mpz_set_ui(true_value->val, 1);
|
||||||
|
|
||||||
commodity_base_t::updater = NULL;
|
|
||||||
|
|
||||||
commodity_t::commodities_by_ident = new commodities_array;
|
|
||||||
|
|
||||||
commodity_t::default_commodity = NULL;
|
|
||||||
commodity_t::null_commodity = commodity_t::create("");
|
|
||||||
commodity_t::null_commodity->add_flags(COMMODITY_STYLE_NOMARKET |
|
|
||||||
COMMODITY_STYLE_BUILTIN);
|
|
||||||
|
|
||||||
// Add time commodity conversions, so that timelog's may be parsed
|
|
||||||
// in terms of seconds, but reported as minutes or hours.
|
|
||||||
commodity_t * commodity = commodity_t::create("s");
|
|
||||||
commodity->add_flags(COMMODITY_STYLE_NOMARKET | COMMODITY_STYLE_BUILTIN);
|
|
||||||
|
|
||||||
parse_conversion("1.0m", "60s");
|
|
||||||
parse_conversion("1.0h", "60m");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void amount_t::shutdown()
|
void amount_t::shutdown()
|
||||||
|
|
@ -135,30 +124,12 @@ void amount_t::shutdown()
|
||||||
mpz_clear(temp);
|
mpz_clear(temp);
|
||||||
mpz_clear(divisor);
|
mpz_clear(divisor);
|
||||||
|
|
||||||
if (commodity_base_t::updater) {
|
// jww (2007-05-02): Be very careful here!
|
||||||
checked_delete(commodity_base_t::updater);
|
if (default_pool) {
|
||||||
commodity_base_t::updater = NULL;
|
checked_delete(default_pool);
|
||||||
|
default_pool = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (base_commodities_map::iterator i = commodity_base_t::commodities.begin();
|
|
||||||
i != commodity_base_t::commodities.end();
|
|
||||||
i++)
|
|
||||||
checked_delete((*i).second);
|
|
||||||
|
|
||||||
for (commodities_map::iterator i = commodity_t::commodities.begin();
|
|
||||||
i != commodity_t::commodities.end();
|
|
||||||
i++)
|
|
||||||
checked_delete((*i).second);
|
|
||||||
|
|
||||||
commodity_base_t::commodities.clear();
|
|
||||||
commodity_t::commodities.clear();
|
|
||||||
|
|
||||||
checked_delete(commodity_t::commodities_by_ident);
|
|
||||||
commodity_t::commodities_by_ident = NULL;
|
|
||||||
|
|
||||||
commodity_t::null_commodity = NULL;
|
|
||||||
commodity_t::default_commodity = NULL;
|
|
||||||
|
|
||||||
true_value->ref--;
|
true_value->ref--;
|
||||||
assert(true_value->ref == 0);
|
assert(true_value->ref == 0);
|
||||||
checked_delete(true_value);
|
checked_delete(true_value);
|
||||||
|
|
@ -408,9 +379,9 @@ amount_t& amount_t::operator+=(const amount_t& amt)
|
||||||
if (commodity() != amt.commodity())
|
if (commodity() != amt.commodity())
|
||||||
throw_(amount_error,
|
throw_(amount_error,
|
||||||
"Adding amounts with different commodities: " <<
|
"Adding amounts with different commodities: " <<
|
||||||
(has_commodity() ? commodity_->qualified_symbol : "NONE") <<
|
(has_commodity() ? commodity().symbol() : "NONE") <<
|
||||||
" != " <<
|
" != " <<
|
||||||
(amt.has_commodity() ? amt.commodity_->qualified_symbol : "NONE"));
|
(amt.has_commodity() ? amt.commodity().symbol() : "NONE"));
|
||||||
|
|
||||||
if (! amt.quantity)
|
if (! amt.quantity)
|
||||||
return *this;
|
return *this;
|
||||||
|
|
@ -443,9 +414,9 @@ amount_t& amount_t::operator-=(const amount_t& amt)
|
||||||
if (commodity() != amt.commodity())
|
if (commodity() != amt.commodity())
|
||||||
throw_(amount_error,
|
throw_(amount_error,
|
||||||
"Subtracting amounts with different commodities: " <<
|
"Subtracting amounts with different commodities: " <<
|
||||||
(has_commodity() ? commodity_->qualified_symbol : "NONE") <<
|
(has_commodity() ? commodity().symbol() : "NONE") <<
|
||||||
" != " <<
|
" != " <<
|
||||||
(amt.has_commodity() ? amt.commodity_->qualified_symbol : "NONE"));
|
(amt.has_commodity() ? amt.commodity().symbol() : "NONE"));
|
||||||
|
|
||||||
if (! amt.quantity)
|
if (! amt.quantity)
|
||||||
return *this;
|
return *this;
|
||||||
|
|
@ -529,9 +500,9 @@ amount_t& amount_t::operator*=(const amount_t& amt)
|
||||||
commodity() != amt.commodity())
|
commodity() != amt.commodity())
|
||||||
throw_(amount_error,
|
throw_(amount_error,
|
||||||
"Multiplying amounts with different commodities: " <<
|
"Multiplying amounts with different commodities: " <<
|
||||||
(has_commodity() ? commodity_->qualified_symbol : "NONE") <<
|
(has_commodity() ? commodity().symbol() : "NONE") <<
|
||||||
" != " <<
|
" != " <<
|
||||||
(amt.has_commodity() ? amt.commodity_->qualified_symbol : "NONE"));
|
(amt.has_commodity() ? amt.commodity().symbol() : "NONE"));
|
||||||
|
|
||||||
if (! amt.quantity) {
|
if (! amt.quantity) {
|
||||||
*this = *this - *this; // preserve our commodity
|
*this = *this - *this; // preserve our commodity
|
||||||
|
|
@ -569,9 +540,9 @@ amount_t& amount_t::operator/=(const amount_t& amt)
|
||||||
commodity() != amt.commodity())
|
commodity() != amt.commodity())
|
||||||
throw_(amount_error,
|
throw_(amount_error,
|
||||||
"Dividing amounts with different commodities: " <<
|
"Dividing amounts with different commodities: " <<
|
||||||
(has_commodity() ? commodity_->qualified_symbol : "NONE") <<
|
(has_commodity() ? commodity().symbol() : "NONE") <<
|
||||||
" != " <<
|
" != " <<
|
||||||
(amt.has_commodity() ? amt.commodity_->qualified_symbol : "NONE"));
|
(amt.has_commodity() ? amt.commodity().symbol() : "NONE"));
|
||||||
|
|
||||||
if (! amt.quantity || ! amt) {
|
if (! amt.quantity || ! amt) {
|
||||||
throw_(amount_error, "Divide by zero");
|
throw_(amount_error, "Divide by zero");
|
||||||
|
|
@ -686,14 +657,14 @@ amount_t& amount_t::in_place_unreduce()
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
amount_t amount_t::value(const moment_t& moment) const
|
optional<amount_t> amount_t::value(const optional<moment_t>& moment) const
|
||||||
{
|
{
|
||||||
if (quantity) {
|
if (quantity) {
|
||||||
amount_t amt(commodity().value(moment));
|
optional<amount_t> amt(commodity().value(moment));
|
||||||
if (! amt.realzero())
|
if (amt)
|
||||||
return (amt * number()).round();
|
return (*amt * number()).round();
|
||||||
}
|
}
|
||||||
return *this;
|
return optional<amount_t>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -756,34 +727,29 @@ long amount_t::to_long() const
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void amount_t::annotate_commodity(const optional<amount_t>& tprice,
|
void amount_t::annotate_commodity(const annotation_t& details)
|
||||||
const optional<moment_t>& tdate,
|
|
||||||
const optional<string>& ttag)
|
|
||||||
{
|
{
|
||||||
const commodity_t * this_base;
|
commodity_t * this_base;
|
||||||
annotated_commodity_t * this_ann = NULL;
|
annotated_commodity_t * this_ann = NULL;
|
||||||
|
|
||||||
if (commodity().annotated) {
|
if (commodity().annotated) {
|
||||||
this_ann = &static_cast<annotated_commodity_t&>(commodity());
|
this_ann = &commodity().as_annotated();
|
||||||
this_base = this_ann->ptr;
|
this_base = &this_ann->referent();
|
||||||
} else {
|
} else {
|
||||||
this_base = &commodity();
|
this_base = &commodity();
|
||||||
}
|
}
|
||||||
assert(this_base);
|
assert(this_base);
|
||||||
|
|
||||||
DEBUG("amounts.commodities", "Annotating commodity for amount "
|
DEBUG("amounts.commodities", "Annotating commodity for amount "
|
||||||
<< *this << std::endl
|
<< *this << std::endl << details);
|
||||||
<< " price " << (tprice ? tprice->to_string() : "NONE") << " "
|
|
||||||
<< " date " << (tdate ? *tdate : moment_t()) << " "
|
|
||||||
<< " ttag " << (ttag ? *ttag : "NONE"));
|
|
||||||
|
|
||||||
if (commodity_t * ann_comm =
|
if (commodity_t * ann_comm =
|
||||||
annotated_commodity_t::find_or_create
|
this_base->parent().find_or_create(*this_base, details))
|
||||||
(*this_base,
|
|
||||||
! tprice && this_ann ? this_ann->price : tprice,
|
|
||||||
! tdate && this_ann ? this_ann->date : tdate,
|
|
||||||
! ttag && this_ann ? this_ann->tag : ttag))
|
|
||||||
set_commodity(*ann_comm);
|
set_commodity(*ann_comm);
|
||||||
|
#ifdef ASSERTS_ON
|
||||||
|
else
|
||||||
|
assert(false);
|
||||||
|
#endif
|
||||||
|
|
||||||
DEBUG("amounts.commodities", " Annotated amount is " << *this);
|
DEBUG("amounts.commodities", " Annotated amount is " << *this);
|
||||||
}
|
}
|
||||||
|
|
@ -802,69 +768,48 @@ amount_t amount_t::strip_annotations(const bool _keep_price,
|
||||||
<< " keep date " << _keep_date << " "
|
<< " keep date " << _keep_date << " "
|
||||||
<< " keep tag " << _keep_tag);
|
<< " keep tag " << _keep_tag);
|
||||||
|
|
||||||
annotated_commodity_t&
|
annotated_commodity_t& ann_comm(commodity().as_annotated());
|
||||||
ann_comm(static_cast<annotated_commodity_t&>(commodity()));
|
commodity_t * new_comm;
|
||||||
assert(ann_comm.base);
|
|
||||||
|
|
||||||
commodity_t * new_comm;
|
if ((_keep_price && ann_comm.details.price) ||
|
||||||
|
(_keep_date && ann_comm.details.date) ||
|
||||||
if ((_keep_price && ann_comm.price) ||
|
(_keep_tag && ann_comm.details.tag))
|
||||||
(_keep_date && ann_comm.date) ||
|
|
||||||
(_keep_tag && ann_comm.tag))
|
|
||||||
{
|
{
|
||||||
new_comm = annotated_commodity_t::find_or_create
|
new_comm = ann_comm.parent().find_or_create
|
||||||
(*ann_comm.ptr,
|
(ann_comm.referent(),
|
||||||
_keep_price ? ann_comm.price : optional<amount_t>(),
|
annotation_t(_keep_price ? ann_comm.details.price : optional<amount_t>(),
|
||||||
_keep_date ? ann_comm.date : optional<moment_t>(),
|
_keep_date ? ann_comm.details.date : optional<moment_t>(),
|
||||||
_keep_tag ? ann_comm.tag : optional<string>());
|
_keep_tag ? ann_comm.details.tag : optional<string>()));
|
||||||
} else {
|
} else {
|
||||||
new_comm = commodity_t::find_or_create(ann_comm.base_symbol());
|
new_comm = ann_comm.parent().find_or_create(ann_comm.base_symbol());
|
||||||
}
|
}
|
||||||
assert(new_comm);
|
assert(new_comm);
|
||||||
|
|
||||||
amount_t t(*this);
|
amount_t t(*this);
|
||||||
t.set_commodity(*new_comm);
|
t.set_commodity(*new_comm);
|
||||||
DEBUG("amounts.commodities", " Reduced amount is " << t);
|
|
||||||
|
DEBUG("amounts.commodities", " Stripped amount is " << t);
|
||||||
|
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
optional<amount_t> amount_t::price() const
|
bool amount_t::commodity_annotated() const
|
||||||
{
|
{
|
||||||
if (commodity_ && commodity_->annotated &&
|
assert(! commodity().annotated || commodity().as_annotated().details);
|
||||||
((annotated_commodity_t *)commodity_)->price) {
|
return commodity().annotated;
|
||||||
amount_t t(*((annotated_commodity_t *)commodity_)->price);
|
|
||||||
t *= number();
|
|
||||||
DEBUG("amounts.commodities",
|
|
||||||
"Returning price of " << *this << " = " << t);
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
return optional<amount_t>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
optional<moment_t> amount_t::date() const
|
annotation_t amount_t::annotation_details() const
|
||||||
{
|
{
|
||||||
if (commodity_ && commodity_->annotated) {
|
assert(! commodity().annotated || commodity().as_annotated().details);
|
||||||
DEBUG("amounts.commodities",
|
|
||||||
"Returning date of " << *this << " = "
|
|
||||||
<< ((annotated_commodity_t *)commodity_)->date);
|
|
||||||
return ((annotated_commodity_t *)commodity_)->date;
|
|
||||||
}
|
|
||||||
return optional<moment_t>();
|
|
||||||
}
|
|
||||||
|
|
||||||
optional<string> amount_t::tag() const
|
if (commodity().annotated) {
|
||||||
{
|
annotated_commodity_t& ann_comm(commodity().as_annotated());
|
||||||
if (commodity_ && commodity_->annotated) {
|
return ann_comm.details;
|
||||||
DEBUG("amounts.commodities",
|
|
||||||
"Returning tag of " << *this << " = "
|
|
||||||
<< ((annotated_commodity_t *)commodity_)->tag);
|
|
||||||
return ((annotated_commodity_t *)commodity_)->tag;
|
|
||||||
}
|
}
|
||||||
return optional<string>();
|
return annotation_t();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void parse_quantity(std::istream& in, string& value)
|
static void parse_quantity(std::istream& in, string& value)
|
||||||
{
|
{
|
||||||
char buf[256];
|
char buf[256];
|
||||||
|
|
@ -923,16 +868,15 @@ static void parse_commodity(std::istream& in, string& symbol)
|
||||||
symbol = buf;
|
symbol = buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
void parse_annotations(std::istream& in,
|
bool parse_annotations(commodity_pool_t& parent,
|
||||||
optional<amount_t>& price,
|
std::istream& in,
|
||||||
optional<moment_t>& date,
|
annotation_t& details)
|
||||||
optional<string>& tag)
|
|
||||||
{
|
{
|
||||||
do {
|
do {
|
||||||
char buf[256];
|
char buf[256];
|
||||||
char c = peek_next_nonws(in);
|
char c = peek_next_nonws(in);
|
||||||
if (c == '{') {
|
if (c == '{') {
|
||||||
if (price)
|
if (details.price)
|
||||||
throw_(amount_error, "Commodity specifies more than one price");
|
throw_(amount_error, "Commodity specifies more than one price");
|
||||||
|
|
||||||
in.get(c);
|
in.get(c);
|
||||||
|
|
@ -943,7 +887,7 @@ void parse_annotations(std::istream& in,
|
||||||
throw_(amount_error, "Commodity price lacks closing brace");
|
throw_(amount_error, "Commodity price lacks closing brace");
|
||||||
|
|
||||||
amount_t temp;
|
amount_t temp;
|
||||||
temp.parse(buf, AMOUNT_PARSE_NO_MIGRATE);
|
temp.parse(parent, buf, AMOUNT_PARSE_NO_MIGRATE);
|
||||||
temp.in_place_reduce();
|
temp.in_place_reduce();
|
||||||
|
|
||||||
// Since this price will maintain its own precision, make sure
|
// Since this price will maintain its own precision, make sure
|
||||||
|
|
@ -954,10 +898,10 @@ void parse_annotations(std::istream& in,
|
||||||
temp.quantity->prec < temp.commodity().precision())
|
temp.quantity->prec < temp.commodity().precision())
|
||||||
temp = temp.round(); // no need to retain individual precision
|
temp = temp.round(); // no need to retain individual precision
|
||||||
|
|
||||||
price = temp;
|
details.price = temp;
|
||||||
}
|
}
|
||||||
else if (c == '[') {
|
else if (c == '[') {
|
||||||
if (date)
|
if (details.date)
|
||||||
throw_(amount_error, "Commodity specifies more than one date");
|
throw_(amount_error, "Commodity specifies more than one date");
|
||||||
|
|
||||||
in.get(c);
|
in.get(c);
|
||||||
|
|
@ -967,10 +911,10 @@ void parse_annotations(std::istream& in,
|
||||||
else
|
else
|
||||||
throw_(amount_error, "Commodity date lacks closing bracket");
|
throw_(amount_error, "Commodity date lacks closing bracket");
|
||||||
|
|
||||||
date = parse_datetime(buf);
|
details.date = parse_datetime(buf);
|
||||||
}
|
}
|
||||||
else if (c == '(') {
|
else if (c == '(') {
|
||||||
if (tag)
|
if (details.tag)
|
||||||
throw_(amount_error, "Commodity specifies more than one tag");
|
throw_(amount_error, "Commodity specifies more than one tag");
|
||||||
|
|
||||||
in.get(c);
|
in.get(c);
|
||||||
|
|
@ -980,7 +924,7 @@ void parse_annotations(std::istream& in,
|
||||||
else
|
else
|
||||||
throw_(amount_error, "Commodity tag lacks closing parenthesis");
|
throw_(amount_error, "Commodity tag lacks closing parenthesis");
|
||||||
|
|
||||||
tag = buf;
|
details.tag = buf;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
break;
|
break;
|
||||||
|
|
@ -989,25 +933,28 @@ void parse_annotations(std::istream& in,
|
||||||
|
|
||||||
DEBUG("amounts.commodities",
|
DEBUG("amounts.commodities",
|
||||||
"Parsed commodity annotations: "
|
"Parsed commodity annotations: "
|
||||||
<< " price " << (price ? price->to_string() : "NONE") << " "
|
<< " price "
|
||||||
<< " date " << (date ? *date : moment_t()) << " "
|
<< (details.price ? details.price->to_string() : "NONE") << " "
|
||||||
<< " tag " << (tag ? *tag : "NONE"));
|
<< " date "
|
||||||
|
<< (details.date ? *details.date : moment_t()) << " "
|
||||||
|
<< " tag "
|
||||||
|
<< (details.tag ? *details.tag : "NONE"));
|
||||||
|
|
||||||
|
return details;
|
||||||
}
|
}
|
||||||
|
|
||||||
void amount_t::parse(std::istream& in, uint8_t flags)
|
void amount_t::parse(commodity_pool_t& parent, std::istream& in, uint8_t flags)
|
||||||
{
|
{
|
||||||
// The possible syntax for an amount is:
|
// The possible syntax for an amount is:
|
||||||
//
|
//
|
||||||
// [-]NUM[ ]SYM [@ AMOUNT]
|
// [-]NUM[ ]SYM [@ AMOUNT]
|
||||||
// SYM[ ][-]NUM [@ AMOUNT]
|
// SYM[ ][-]NUM [@ AMOUNT]
|
||||||
|
|
||||||
string symbol;
|
string symbol;
|
||||||
string quant;
|
string quant;
|
||||||
optional<amount_t> tprice;
|
annotation_t details;
|
||||||
optional<moment_t> tdate;
|
unsigned int comm_flags = COMMODITY_STYLE_DEFAULTS;
|
||||||
optional<string> ttag;
|
bool negative = false;
|
||||||
unsigned int comm_flags = COMMODITY_STYLE_DEFAULTS;
|
|
||||||
bool negative = false;
|
|
||||||
|
|
||||||
char c = peek_next_nonws(in);
|
char c = peek_next_nonws(in);
|
||||||
if (c == '-') {
|
if (c == '-') {
|
||||||
|
|
@ -1030,7 +977,7 @@ void amount_t::parse(std::istream& in, uint8_t flags)
|
||||||
comm_flags |= COMMODITY_STYLE_SUFFIXED;
|
comm_flags |= COMMODITY_STYLE_SUFFIXED;
|
||||||
|
|
||||||
if (! in.eof() && ((n = in.peek()) != '\n'))
|
if (! in.eof() && ((n = in.peek()) != '\n'))
|
||||||
parse_annotations(in, tprice, tdate, ttag);
|
parse_annotations(parent, in, details);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
parse_commodity(in, symbol);
|
parse_commodity(in, symbol);
|
||||||
|
|
@ -1042,7 +989,7 @@ void amount_t::parse(std::istream& in, uint8_t flags)
|
||||||
parse_quantity(in, quant);
|
parse_quantity(in, quant);
|
||||||
|
|
||||||
if (! quant.empty() && ! in.eof() && ((n = in.peek()) != '\n'))
|
if (! quant.empty() && ! in.eof() && ((n = in.peek()) != '\n'))
|
||||||
parse_annotations(in, tprice, tdate, ttag);
|
parse_annotations(parent, in, details);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1059,16 +1006,15 @@ void amount_t::parse(std::istream& in, uint8_t flags)
|
||||||
if (symbol.empty()) {
|
if (symbol.empty()) {
|
||||||
commodity_ = NULL;
|
commodity_ = NULL;
|
||||||
} else {
|
} else {
|
||||||
commodity_ = commodity_t::find(symbol);
|
commodity_ = parent.find(symbol);
|
||||||
if (! commodity_) {
|
if (! commodity_) {
|
||||||
commodity_ = commodity_t::create(symbol);
|
commodity_ = parent.create(symbol);
|
||||||
newly_created = true;
|
newly_created = true;
|
||||||
}
|
}
|
||||||
assert(commodity_);
|
assert(commodity_);
|
||||||
|
|
||||||
if (tprice || tdate || ttag)
|
if (details)
|
||||||
commodity_ = annotated_commodity_t::find_or_create
|
commodity_ = parent.find_or_create(*commodity_, details);
|
||||||
(*commodity_, tprice, tdate, ttag);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine the precision of the amount, based on the usage of
|
// Determine the precision of the amount, based on the usage of
|
||||||
|
|
@ -1100,7 +1046,8 @@ void amount_t::parse(std::istream& in, uint8_t flags)
|
||||||
|
|
||||||
// Set the commodity's flags and precision accordingly
|
// Set the commodity's flags and precision accordingly
|
||||||
|
|
||||||
if (commodity_ && (newly_created || ! (flags & AMOUNT_PARSE_NO_MIGRATE))) {
|
if (commodity_ &&
|
||||||
|
(newly_created || ! (flags & AMOUNT_PARSE_NO_MIGRATE))) {
|
||||||
commodity().add_flags(comm_flags);
|
commodity().add_flags(comm_flags);
|
||||||
if (quantity->prec > commodity().precision())
|
if (quantity->prec > commodity().precision())
|
||||||
commodity().set_precision(quantity->prec);
|
commodity().set_precision(quantity->prec);
|
||||||
|
|
@ -1138,13 +1085,14 @@ void amount_t::parse(std::istream& in, uint8_t flags)
|
||||||
in_place_reduce();
|
in_place_reduce();
|
||||||
}
|
}
|
||||||
|
|
||||||
void amount_t::parse_conversion(const string& larger_str,
|
void amount_t::parse_conversion(commodity_pool_t& parent,
|
||||||
|
const string& larger_str,
|
||||||
const string& smaller_str)
|
const string& smaller_str)
|
||||||
{
|
{
|
||||||
amount_t larger, smaller;
|
amount_t larger, smaller;
|
||||||
|
|
||||||
larger.parse(larger_str, AMOUNT_PARSE_NO_REDUCE);
|
larger.parse(parent, larger_str, AMOUNT_PARSE_NO_REDUCE);
|
||||||
smaller.parse(smaller_str, AMOUNT_PARSE_NO_REDUCE);
|
smaller.parse(parent, smaller_str, AMOUNT_PARSE_NO_REDUCE);
|
||||||
|
|
||||||
larger *= smaller.number();
|
larger *= smaller.number();
|
||||||
|
|
||||||
|
|
@ -1323,7 +1271,7 @@ void amount_t::print(std::ostream& _out, bool omit_commodity,
|
||||||
|
|
||||||
if (! omit_commodity && comm.annotated) {
|
if (! omit_commodity && comm.annotated) {
|
||||||
annotated_commodity_t& ann(static_cast<annotated_commodity_t&>(comm));
|
annotated_commodity_t& ann(static_cast<annotated_commodity_t&>(comm));
|
||||||
assert(&*ann.price != this);
|
assert(&*ann.details.price != this);
|
||||||
ann.write_annotations(out);
|
ann.write_annotations(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1337,30 +1285,34 @@ void amount_t::print(std::ostream& _out, bool omit_commodity,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void amount_t::read(std::istream& in)
|
void amount_t::read(commodity_pool_t& parent, std::istream& in)
|
||||||
{
|
{
|
||||||
commodity_t::ident_t ident;
|
commodity_t::ident_t ident;
|
||||||
read_binary_long(in, ident);
|
read_binary_long(in, ident);
|
||||||
if (ident == 0xffffffff)
|
if (ident == 0xffffffff)
|
||||||
commodity_ = NULL;
|
commodity_ = NULL;
|
||||||
else if (ident == 0)
|
else if (ident == 0)
|
||||||
commodity_ = commodity_t::null_commodity;
|
commodity_ = parent.null_commodity;
|
||||||
else
|
else {
|
||||||
commodity_ = (*commodity_t::commodities_by_ident)[ident - 1];
|
commodity_ = parent.find(ident - 1);
|
||||||
|
assert(commodity_);
|
||||||
|
}
|
||||||
|
|
||||||
read_quantity(in);
|
read_quantity(in);
|
||||||
}
|
}
|
||||||
|
|
||||||
void amount_t::read(char *& data)
|
void amount_t::read(commodity_pool_t& parent, char *& data)
|
||||||
{
|
{
|
||||||
commodity_t::ident_t ident;
|
commodity_t::ident_t ident;
|
||||||
read_binary_long(data, ident);
|
read_binary_long(data, ident);
|
||||||
if (ident == 0xffffffff)
|
if (ident == 0xffffffff)
|
||||||
commodity_ = NULL;
|
commodity_ = NULL;
|
||||||
else if (ident == 0)
|
else if (ident == 0)
|
||||||
commodity_ = commodity_t::null_commodity;
|
commodity_ = parent.null_commodity;
|
||||||
else
|
else {
|
||||||
commodity_ = (*commodity_t::commodities_by_ident)[ident - 1];
|
commodity_ = parent.find(ident - 1);
|
||||||
|
assert(commodity_);
|
||||||
|
}
|
||||||
|
|
||||||
read_quantity(data);
|
read_quantity(data);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
152
src/amount.h
152
src/amount.h
|
|
@ -52,6 +52,8 @@
|
||||||
namespace ledger {
|
namespace ledger {
|
||||||
|
|
||||||
class commodity_t;
|
class commodity_t;
|
||||||
|
class annotation_t;
|
||||||
|
class commodity_pool_t;
|
||||||
|
|
||||||
DECLARE_EXCEPTION(amount_error);
|
DECLARE_EXCEPTION(amount_error);
|
||||||
|
|
||||||
|
|
@ -87,6 +89,12 @@ public:
|
||||||
static void initialize();
|
static void initialize();
|
||||||
static void shutdown();
|
static void shutdown();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default_pool is a static variable indicating which commodity
|
||||||
|
* pool should be used when none is specified.
|
||||||
|
*/
|
||||||
|
static commodity_pool_t * default_pool;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `keep_base' member determines whether scalable commodities
|
* The `keep_base' member determines whether scalable commodities
|
||||||
* are automatically converted to their most reduced form when
|
* are automatically converted to their most reduced form when
|
||||||
|
|
@ -291,11 +299,12 @@ public:
|
||||||
* compact form greater than 1.0. That is, 3599s will unreduce to
|
* compact form greater than 1.0. That is, 3599s will unreduce to
|
||||||
* 59.98m, while 3601 unreduces to 1h.
|
* 59.98m, while 3601 unreduces to 1h.
|
||||||
*
|
*
|
||||||
* value(moment_t) returns the history value of an amount, based on
|
* value(optional<moment_t>) returns the historical value for an
|
||||||
* the price history of its commodity. For example, if the amount
|
* amount -- the default moment returns the most recently known
|
||||||
* were 10 AAPL, and on Apr 10, 2000 each share of AAPL was worth
|
* price -- based on the price history of its commodity. For
|
||||||
* $10, then call value() for that moment in time would yield the
|
* example, if the amount were 10 AAPL, and on Apr 10, 2000 each
|
||||||
* amount $100.00.
|
* share of AAPL was worth $10, then call value() for that moment in
|
||||||
|
* time would yield the amount $100.00.
|
||||||
*
|
*
|
||||||
* Further, for the sake of efficiency and avoiding temporary
|
* Further, for the sake of efficiency and avoiding temporary
|
||||||
* objects, the following methods support "in-place" variants that
|
* objects, the following methods support "in-place" variants that
|
||||||
|
|
@ -341,7 +350,8 @@ public:
|
||||||
}
|
}
|
||||||
amount_t& in_place_unreduce();
|
amount_t& in_place_unreduce();
|
||||||
|
|
||||||
amount_t value(const moment_t& moment) const;
|
optional<amount_t> value(const optional<moment_t>& moment =
|
||||||
|
optional<moment_t>()) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Truth tests. An amount may be truth test in several ways:
|
* Truth tests. An amount may be truth test in several ways:
|
||||||
|
|
@ -466,38 +476,26 @@ public:
|
||||||
* the price argument is required, although it can be passed as
|
* the price argument is required, although it can be passed as
|
||||||
* `optional<amount_t>()' if no price is desired.
|
* `optional<amount_t>()' if no price is desired.
|
||||||
*
|
*
|
||||||
|
* commodity_annotated() returns true if an amount's commodity has
|
||||||
|
* any annotation details associated with it.
|
||||||
|
*
|
||||||
|
* annotation_details() returns all of the details of an annotated
|
||||||
|
* commodity's annotations. The structure returns will evaluate as
|
||||||
|
* boolean false if there are no details.
|
||||||
|
*
|
||||||
* strip_annotations([keep_price, keep_date, keep_tag]) returns an
|
* strip_annotations([keep_price, keep_date, keep_tag]) returns an
|
||||||
* amount whose commodity's annotations have been stripped. The
|
* amount whose commodity's annotations have been stripped. The
|
||||||
* three `keep_' arguments determine which annotation detailed are
|
* three `keep_' arguments determine which annotation detailed are
|
||||||
* kept, meaning that the default is to follow whatever
|
* kept, meaning that the default is to follow whatever
|
||||||
* amount_t::keep_price, amount_t::keep_date and amount_t::keep_tag
|
* amount_t::keep_price, amount_t::keep_date and amount_t::keep_tag
|
||||||
* have been set to (which all default to false).
|
* have been set to (which all default to false).
|
||||||
*
|
|
||||||
* price() returns an amount's annotated commodity's price. This
|
|
||||||
* return value is of type `optional<amount_t>', so it must be
|
|
||||||
* tested for boolean truth to determine if an annotated price even
|
|
||||||
* existed.
|
|
||||||
*
|
|
||||||
* date() returns an amount's annotated commodity's date. This
|
|
||||||
* return value is of type `optional<moment_t>'.
|
|
||||||
*
|
|
||||||
* tag() returns an amount's annotated commodity's tag. This return
|
|
||||||
* value is of type `optional<string>'.
|
|
||||||
*/
|
*/
|
||||||
void annotate_commodity(const optional<amount_t>& tprice,
|
void annotate_commodity(const annotation_t& details);
|
||||||
const optional<moment_t>& tdate = optional<moment_t>(),
|
bool commodity_annotated() const;
|
||||||
const optional<string>& ttag = optional<string>());
|
annotation_t annotation_details() const;
|
||||||
|
amount_t strip_annotations(const bool _keep_price = keep_price,
|
||||||
amount_t strip_annotations(const bool _keep_price = keep_price,
|
const bool _keep_date = keep_date,
|
||||||
const bool _keep_date = keep_date,
|
const bool _keep_tag = keep_tag) const;
|
||||||
const bool _keep_tag = keep_tag) const;
|
|
||||||
|
|
||||||
optional<amount_t> price() const;
|
|
||||||
optional<moment_t> date() const;
|
|
||||||
optional<string> tag() const;
|
|
||||||
|
|
||||||
#define AMOUNT_PARSE_NO_MIGRATE 0x01
|
|
||||||
#define AMOUNT_PARSE_NO_REDUCE 0x02
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parsing methods. The method `parse' is used to parse an amount
|
* Parsing methods. The method `parse' is used to parse an amount
|
||||||
|
|
@ -505,11 +503,18 @@ public:
|
||||||
* defined which simply calls parse on the input stream. The
|
* defined which simply calls parse on the input stream. The
|
||||||
* `parse' method has two forms:
|
* `parse' method has two forms:
|
||||||
*
|
*
|
||||||
* parse(istream, unsigned char flags) parses an amount from the
|
* parse(commodity_pool_t, istream, flags_t) parses an
|
||||||
* given input stream.
|
* amount from the given input stream, registering commodity details
|
||||||
|
* according to the commodity pool which is passed in as the first
|
||||||
|
* parameter.
|
||||||
*
|
*
|
||||||
* parse(string, unsigned char flags) parses an amount from the
|
* parse(istream, flags_t) is the same as the preceding function,
|
||||||
* given string.
|
* only it uses `amount_t::default_pool' as the commodity pool.
|
||||||
|
*
|
||||||
|
* parse(commodity_pool_t, string, flags_t) parses an amount from
|
||||||
|
* the given string.
|
||||||
|
*
|
||||||
|
* parse(string, flags_t) also parses an amount from a string.
|
||||||
*
|
*
|
||||||
* The `flags' argument of both parsing may be one or more of the
|
* The `flags' argument of both parsing may be one or more of the
|
||||||
* following:
|
* following:
|
||||||
|
|
@ -538,15 +543,36 @@ public:
|
||||||
* amount_t::parse_conversion("1.0m", "60s"); // a minute is 60 seconds
|
* amount_t::parse_conversion("1.0m", "60s"); // a minute is 60 seconds
|
||||||
* amount_t::parse_conversion("1.0h", "60m"); // an hour is 60 minutes
|
* amount_t::parse_conversion("1.0h", "60m"); // an hour is 60 minutes
|
||||||
*/
|
*/
|
||||||
void parse(std::istream& in, unsigned char flags = 0);
|
#define AMOUNT_PARSE_NO_MIGRATE 0x01
|
||||||
void parse(const string& str, unsigned char flags = 0) {
|
#define AMOUNT_PARSE_NO_REDUCE 0x02
|
||||||
|
|
||||||
|
typedef uint_least8_t flags_t;
|
||||||
|
|
||||||
|
void parse(commodity_pool_t& parent, std::istream& in, flags_t flags = 0);
|
||||||
|
void parse(commodity_pool_t& parent, const string& str, flags_t flags = 0) {
|
||||||
std::istringstream stream(str);
|
std::istringstream stream(str);
|
||||||
parse(stream, flags);
|
parse(parent, stream, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void parse_conversion(const string& larger_str,
|
void parse(std::istream& in, flags_t flags = 0) {
|
||||||
|
assert(default_pool);
|
||||||
|
parse(*default_pool, in, flags);
|
||||||
|
}
|
||||||
|
void parse(const string& str, flags_t flags = 0) {
|
||||||
|
assert(default_pool);
|
||||||
|
parse(*default_pool, str, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void parse_conversion(commodity_pool_t& parent,
|
||||||
|
const string& larger_str,
|
||||||
const string& smaller_str);
|
const string& smaller_str);
|
||||||
|
|
||||||
|
static void parse_conversion(const string& larger_str,
|
||||||
|
const string& smaller_str) {
|
||||||
|
assert(default_pool);
|
||||||
|
parse_conversion(*default_pool, larger_str, smaller_str);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Printing methods. An amount may be output to a stream using the
|
* Printing methods. An amount may be output to a stream using the
|
||||||
* `print' method. There is also a global operator<< defined which
|
* `print' method. There is also a global operator<< defined which
|
||||||
|
|
@ -571,18 +597,40 @@ public:
|
||||||
* input stream or a character pointer, and it may be serialized to
|
* input stream or a character pointer, and it may be serialized to
|
||||||
* an output stream. The methods used are:
|
* an output stream. The methods used are:
|
||||||
*
|
*
|
||||||
* read(istream) reads an amount from the given input stream. It
|
* read(commodity_pool_t, istream) reads an amount from the given
|
||||||
* must have been put there using `write(ostream)'.
|
* input stream. It must have been put there using
|
||||||
|
* `write(ostream)'. Also, the given pool must be exactly what it
|
||||||
|
* was at the time the amount was `written'. Thus, the required
|
||||||
|
* flow of logic is:
|
||||||
|
* amount.write(out)
|
||||||
|
* pool.write(out)
|
||||||
|
* pool.read(in)
|
||||||
|
* amount.read(pool, in)
|
||||||
*
|
*
|
||||||
* read(char *&) reads an amount from data which has been read from
|
* read(istream) does the same as read, only it relies on
|
||||||
* an input stream into a buffer. it advances the pointer passed in
|
* `amount_t::default_pool' to specify the pool.
|
||||||
* to the end of the deserialized amount.
|
*
|
||||||
|
* read(commodity_pool_t, char *&) reads an amount from data which
|
||||||
|
* has been read from an input stream into a buffer. it advances
|
||||||
|
* the pointer passed in to the end of the deserialized amount.
|
||||||
|
*
|
||||||
|
* read(char *&) does the same as read, only it relies on
|
||||||
|
* `amount_t::default_pool' to specify the pool.
|
||||||
*
|
*
|
||||||
* write(ostream) writes an amount to an output stream in a compact
|
* write(ostream) writes an amount to an output stream in a compact
|
||||||
* binary format.
|
* binary format.
|
||||||
*/
|
*/
|
||||||
void read(std::istream& in);
|
void read(commodity_pool_t& parent, std::istream& in);
|
||||||
void read(char *& data);
|
void read(commodity_pool_t& parent, char *& data);
|
||||||
|
|
||||||
|
void read(std::istream& in) {
|
||||||
|
assert(default_pool);
|
||||||
|
read(*default_pool, in);
|
||||||
|
}
|
||||||
|
void read(char *& data) {
|
||||||
|
assert(default_pool);
|
||||||
|
read(*default_pool, data);
|
||||||
|
}
|
||||||
|
|
||||||
void write(std::ostream& out) const;
|
void write(std::ostream& out) const;
|
||||||
|
|
||||||
|
|
@ -614,10 +662,9 @@ public:
|
||||||
bool valid() const;
|
bool valid() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend void parse_annotations(std::istream& in,
|
friend bool parse_annotations(commodity_pool_t& parent,
|
||||||
optional<amount_t>& price,
|
std::istream& in,
|
||||||
optional<moment_t>& date,
|
annotation_t& details);
|
||||||
optional<string>& tag);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
inline amount_t amount_t::exact(const string& value) {
|
inline amount_t amount_t::exact(const string& value) {
|
||||||
|
|
@ -672,11 +719,12 @@ inline amount_t amount_t::round() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool amount_t::has_commodity() const {
|
inline bool amount_t::has_commodity() const {
|
||||||
return commodity_ && commodity_ != commodity_t::null_commodity;
|
return commodity_ && commodity_ != commodity_->parent().null_commodity;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline commodity_t& amount_t::commodity() const {
|
inline commodity_t& amount_t::commodity() const {
|
||||||
return has_commodity() ? *commodity_ : *commodity_t::null_commodity;
|
// jww (2007-05-02): Should be a way to access null_commodity better
|
||||||
|
return has_commodity() ? *commodity_ : *default_pool->null_commodity;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ledger
|
} // namespace ledger
|
||||||
|
|
|
||||||
128
src/balance.cc
128
src/balance.cc
|
|
@ -133,7 +133,8 @@ balance_t& balance_t::operator/=(const amount_t& amt)
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
amount_t balance_t::amount(const commodity_t& commodity) const
|
optional<amount_t>
|
||||||
|
balance_t::amount(const optional<const commodity_t&>& commodity) const
|
||||||
{
|
{
|
||||||
if (! commodity) {
|
if (! commodity) {
|
||||||
if (amounts.size() == 1) {
|
if (amounts.size() == 1) {
|
||||||
|
|
@ -151,71 +152,27 @@ amount_t balance_t::amount(const commodity_t& commodity) const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (amounts.size() > 0) {
|
else if (amounts.size() > 0) {
|
||||||
amounts_map::const_iterator i = amounts.find(&commodity);
|
amounts_map::const_iterator i = amounts.find(&*commodity);
|
||||||
if (i != amounts.end())
|
if (i != amounts.end())
|
||||||
return (*i).second;
|
return (*i).second;
|
||||||
}
|
}
|
||||||
return amount_t();
|
return optional<amount_t>();
|
||||||
}
|
}
|
||||||
|
|
||||||
balance_t balance_t::value(const moment_t& moment) const
|
optional<balance_t>
|
||||||
{
|
balance_t::value(const optional<moment_t>& moment) const
|
||||||
balance_t temp;
|
|
||||||
|
|
||||||
for (amounts_map::const_iterator i = amounts.begin();
|
|
||||||
i != amounts.end();
|
|
||||||
i++)
|
|
||||||
temp += (*i).second.value(moment);
|
|
||||||
|
|
||||||
return temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
optional<balance_t> balance_t::price() const
|
|
||||||
{
|
{
|
||||||
optional<balance_t> temp;
|
optional<balance_t> temp;
|
||||||
|
|
||||||
for (amounts_map::const_iterator i = amounts.begin();
|
for (amounts_map::const_iterator i = amounts.begin();
|
||||||
i != amounts.end();
|
i != amounts.end();
|
||||||
i++) {
|
i++)
|
||||||
optional<amount_t> i_price = (*i).second.price();
|
if (optional<amount_t> val = (*i).second.value(moment)) {
|
||||||
if (i_price) {
|
|
||||||
if (! temp)
|
if (! temp)
|
||||||
temp = balance_t();
|
temp = balance_t();
|
||||||
*temp += *i_price;
|
*temp += *val;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
optional<moment_t> balance_t::date() const
|
|
||||||
{
|
|
||||||
optional<moment_t> temp;
|
|
||||||
|
|
||||||
for (amounts_map::const_iterator i = amounts.begin();
|
|
||||||
i != amounts.end();
|
|
||||||
i++) {
|
|
||||||
optional<moment_t> tdate = (*i).second.date();
|
|
||||||
if (! temp && tdate)
|
|
||||||
temp = *tdate;
|
|
||||||
else if (temp && tdate && temp != tdate)
|
|
||||||
return optional<moment_t>();
|
|
||||||
}
|
|
||||||
return temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
optional<string> balance_t::tag() const
|
|
||||||
{
|
|
||||||
optional<string> temp;
|
|
||||||
|
|
||||||
for (amounts_map::const_iterator i = amounts.begin();
|
|
||||||
i != amounts.end();
|
|
||||||
i++) {
|
|
||||||
optional<string> ttag = (*i).second.tag();
|
|
||||||
if (! temp && ttag)
|
|
||||||
temp = *ttag;
|
|
||||||
else if (temp && ttag && temp != ttag)
|
|
||||||
return optional<string>();
|
|
||||||
}
|
|
||||||
return temp;
|
return temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -243,52 +200,33 @@ void balance_t::write(std::ostream& out,
|
||||||
if (lwidth == -1)
|
if (lwidth == -1)
|
||||||
lwidth = first_width;
|
lwidth = first_width;
|
||||||
|
|
||||||
if (commodity_t::commodities_sorted) {
|
typedef std::vector<const amount_t *> amounts_array;
|
||||||
for (amounts_map::const_iterator i = amounts.begin();
|
amounts_array sorted;
|
||||||
i != amounts.end();
|
|
||||||
i++) {
|
|
||||||
int width;
|
|
||||||
if (! first) {
|
|
||||||
out << std::endl;
|
|
||||||
width = lwidth;
|
|
||||||
} else {
|
|
||||||
first = false;
|
|
||||||
width = first_width;
|
|
||||||
}
|
|
||||||
|
|
||||||
out.width(width);
|
for (amounts_map::const_iterator i = amounts.begin();
|
||||||
out.fill(' ');
|
i != amounts.end();
|
||||||
out << std::right << (*i).second;
|
i++)
|
||||||
|
if ((*i).second)
|
||||||
|
sorted.push_back(&(*i).second);
|
||||||
|
|
||||||
|
std::stable_sort(sorted.begin(), sorted.end(),
|
||||||
|
compare_amount_commodities());
|
||||||
|
|
||||||
|
for (amounts_array::const_iterator i = sorted.begin();
|
||||||
|
i != sorted.end();
|
||||||
|
i++) {
|
||||||
|
int width;
|
||||||
|
if (! first) {
|
||||||
|
out << std::endl;
|
||||||
|
width = lwidth;
|
||||||
|
} else {
|
||||||
|
first = false;
|
||||||
|
width = first_width;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
typedef std::vector<const amount_t *> amounts_array;
|
|
||||||
amounts_array sorted;
|
|
||||||
|
|
||||||
for (amounts_map::const_iterator i = amounts.begin();
|
out.width(width);
|
||||||
i != amounts.end();
|
out.fill(' ');
|
||||||
i++)
|
out << std::right << **i;
|
||||||
if ((*i).second)
|
|
||||||
sorted.push_back(&(*i).second);
|
|
||||||
|
|
||||||
std::stable_sort(sorted.begin(), sorted.end(),
|
|
||||||
compare_amount_commodities());
|
|
||||||
|
|
||||||
for (amounts_array::const_iterator i = sorted.begin();
|
|
||||||
i != sorted.end();
|
|
||||||
i++) {
|
|
||||||
int width;
|
|
||||||
if (! first) {
|
|
||||||
out << std::endl;
|
|
||||||
width = lwidth;
|
|
||||||
} else {
|
|
||||||
first = false;
|
|
||||||
width = first_width;
|
|
||||||
}
|
|
||||||
|
|
||||||
out.width(width);
|
|
||||||
out.fill(' ');
|
|
||||||
out << std::right << **i;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (first) {
|
if (first) {
|
||||||
|
|
|
||||||
|
|
@ -93,15 +93,19 @@ public:
|
||||||
bool operator<(const balance_t& bal) const {
|
bool operator<(const balance_t& bal) const {
|
||||||
for (amounts_map::const_iterator i = bal.amounts.begin();
|
for (amounts_map::const_iterator i = bal.amounts.begin();
|
||||||
i != bal.amounts.end();
|
i != bal.amounts.end();
|
||||||
i++)
|
i++) {
|
||||||
if (! (amount(*(*i).first) < (*i).second))
|
optional<amount_t> amt = amount(*(*i).first);
|
||||||
|
if (amt && ! (*amt < (*i).second))
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
for (amounts_map::const_iterator i = amounts.begin();
|
for (amounts_map::const_iterator i = amounts.begin();
|
||||||
i != amounts.end();
|
i != amounts.end();
|
||||||
i++)
|
i++) {
|
||||||
if (! ((*i).second < bal.amount(*(*i).first)))
|
optional<amount_t> amt = bal.amount(*(*i).first);
|
||||||
|
if (amt && ! ((*i).second < *amt))
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (bal.amounts.size() == 0 && amounts.size() == 0)
|
if (bal.amounts.size() == 0 && amounts.size() == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -146,13 +150,10 @@ public:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
amount_t amount(const commodity_t& commodity =
|
optional<amount_t> amount(const optional<const commodity_t&>& commodity =
|
||||||
*commodity_t::null_commodity) const;
|
optional<const commodity_t&>()) const;
|
||||||
balance_t value(const moment_t& moment = now) const;
|
optional<balance_t> value(const optional<moment_t>& moment =
|
||||||
|
optional<moment_t>()) const;
|
||||||
optional<balance_t> price() const;
|
|
||||||
optional<moment_t> date() const;
|
|
||||||
optional<string> tag() const;
|
|
||||||
|
|
||||||
balance_t
|
balance_t
|
||||||
strip_annotations(const bool keep_price = amount_t::keep_price,
|
strip_annotations(const bool keep_price = amount_t::keep_price,
|
||||||
|
|
@ -335,24 +336,15 @@ public:
|
||||||
return temp;
|
return temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
amount_t amount(const commodity_t& commodity =
|
optional<amount_t> amount(const optional<const commodity_t&>& commodity =
|
||||||
*commodity_t::null_commodity) const {
|
optional<const commodity_t&>()) const {
|
||||||
return quantity.amount(commodity);
|
return quantity.amount(commodity);
|
||||||
}
|
}
|
||||||
balance_t value(const moment_t& moment = now) const {
|
optional<balance_t> value(const optional<moment_t>& moment =
|
||||||
|
optional<moment_t>()) const {
|
||||||
return quantity.value(moment);
|
return quantity.value(moment);
|
||||||
}
|
}
|
||||||
|
|
||||||
optional<balance_t> price() const {
|
|
||||||
return quantity.price();
|
|
||||||
}
|
|
||||||
optional<moment_t> date() const {
|
|
||||||
return quantity.date();
|
|
||||||
}
|
|
||||||
optional<string> tag() const {
|
|
||||||
return quantity.tag();
|
|
||||||
}
|
|
||||||
|
|
||||||
balance_t
|
balance_t
|
||||||
strip_annotations(const bool keep_price = amount_t::keep_price,
|
strip_annotations(const bool keep_price = amount_t::keep_price,
|
||||||
const bool keep_date = amount_t::keep_date,
|
const bool keep_date = amount_t::keep_date,
|
||||||
|
|
|
||||||
553
src/commodity.cc
553
src/commodity.cc
|
|
@ -43,61 +43,98 @@
|
||||||
|
|
||||||
namespace ledger {
|
namespace ledger {
|
||||||
|
|
||||||
#ifndef THREADSAFE
|
void commodity_t::add_price(const moment_t& date,
|
||||||
base_commodities_map commodity_base_t::commodities;
|
const amount_t& price)
|
||||||
|
|
||||||
commodity_base_t::updater_t * commodity_base_t::updater = NULL;
|
|
||||||
|
|
||||||
commodities_map commodity_t::commodities;
|
|
||||||
commodities_array * commodity_t::commodities_by_ident;
|
|
||||||
bool commodity_t::commodities_sorted = false;
|
|
||||||
commodity_t * commodity_t::null_commodity;
|
|
||||||
commodity_t * commodity_t::default_commodity = NULL;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void commodity_base_t::add_price(const moment_t& date,
|
|
||||||
const amount_t& price)
|
|
||||||
{
|
{
|
||||||
if (! history)
|
if (! base->history)
|
||||||
history = history_t();
|
base->history = history_t();
|
||||||
|
|
||||||
history_map::iterator i = history->prices.find(date);
|
history_map::iterator i = base->history->prices.find(date);
|
||||||
if (i != history->prices.end()) {
|
if (i != base->history->prices.end()) {
|
||||||
(*i).second = price;
|
(*i).second = price;
|
||||||
} else {
|
} else {
|
||||||
std::pair<history_map::iterator, bool> result
|
std::pair<history_map::iterator, bool> result
|
||||||
= history->prices.insert(history_pair(date, price));
|
= base->history->prices.insert(history_pair(date, price));
|
||||||
assert(result.second);
|
assert(result.second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool commodity_base_t::remove_price(const moment_t& date)
|
bool commodity_t::remove_price(const moment_t& date)
|
||||||
{
|
{
|
||||||
if (history) {
|
if (base->history) {
|
||||||
history_map::size_type n = history->prices.erase(date);
|
history_map::size_type n = base->history->prices.erase(date);
|
||||||
if (n > 0) {
|
if (n > 0) {
|
||||||
if (history->prices.empty())
|
if (base->history->prices.empty())
|
||||||
history.reset();
|
base->history.reset();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
commodity_base_t * commodity_base_t::create(const string& symbol)
|
optional<amount_t> commodity_t::value(const optional<moment_t>& moment)
|
||||||
{
|
{
|
||||||
commodity_base_t * commodity = new commodity_base_t(symbol);
|
optional<moment_t> age;
|
||||||
|
optional<amount_t> price;
|
||||||
|
|
||||||
DEBUG("amounts.commodities", "Creating base commodity " << symbol);
|
if (base->history) {
|
||||||
|
assert(base->history->prices.size() > 0);
|
||||||
|
|
||||||
std::pair<base_commodities_map::iterator, bool> result
|
if (! moment) {
|
||||||
= commodities.insert(base_commodities_pair(symbol, commodity));
|
history_map::reverse_iterator r = base->history->prices.rbegin();
|
||||||
assert(result.second);
|
age = (*r).first;
|
||||||
|
price = (*r).second;
|
||||||
|
} else {
|
||||||
|
history_map::iterator i = base->history->prices.lower_bound(*moment);
|
||||||
|
if (i == base->history->prices.end()) {
|
||||||
|
history_map::reverse_iterator r = base->history->prices.rbegin();
|
||||||
|
age = (*r).first;
|
||||||
|
price = (*r).second;
|
||||||
|
} else {
|
||||||
|
age = (*i).first;
|
||||||
|
if (*moment != *age) {
|
||||||
|
if (i != base->history->prices.begin()) {
|
||||||
|
--i;
|
||||||
|
age = (*i).first;
|
||||||
|
price = (*i).second;
|
||||||
|
} else {
|
||||||
|
age = optional<moment_t>();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
price = (*i).second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return commodity;
|
if (! (flags() & COMMODITY_STYLE_NOMARKET)) {
|
||||||
|
if (optional<amount_t> quote = parent().get_quote
|
||||||
|
(*this, age, moment,
|
||||||
|
(base->history && base->history->prices.size() > 0 ?
|
||||||
|
(*base->history->prices.rbegin()).first : optional<moment_t>())))
|
||||||
|
return *quote;
|
||||||
|
}
|
||||||
|
return price;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool commodity_t::needs_quotes(const string& symbol)
|
commodity_t::operator bool() const
|
||||||
|
{
|
||||||
|
return this != parent().null_commodity;
|
||||||
|
}
|
||||||
|
|
||||||
|
annotated_commodity_t& commodity_t::as_annotated()
|
||||||
|
{
|
||||||
|
assert(annotated);
|
||||||
|
return *polymorphic_downcast<annotated_commodity_t *>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
const annotated_commodity_t& commodity_t::as_annotated() const
|
||||||
|
{
|
||||||
|
assert(annotated);
|
||||||
|
return *polymorphic_downcast<const annotated_commodity_t *>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool commodity_t::symbol_needs_quotes(const string& symbol)
|
||||||
{
|
{
|
||||||
for (const char * p = symbol.c_str(); *p; p++)
|
for (const char * p = symbol.c_str(); *p; p++)
|
||||||
if (std::isspace(*p) || std::isdigit(*p) || *p == '-' || *p == '.')
|
if (std::isspace(*p) || std::isdigit(*p) || *p == '-' || *p == '.')
|
||||||
|
|
@ -108,7 +145,7 @@ bool commodity_t::needs_quotes(const string& symbol)
|
||||||
|
|
||||||
bool commodity_t::valid() const
|
bool commodity_t::valid() const
|
||||||
{
|
{
|
||||||
if (symbol().empty() && this != null_commodity) {
|
if (symbol().empty() && this != parent().null_commodity) {
|
||||||
DEBUG("ledger.validate",
|
DEBUG("ledger.validate",
|
||||||
"commodity_t: symbol().empty() && this != null_commodity");
|
"commodity_t: symbol().empty() && this != null_commodity");
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -127,224 +164,34 @@ bool commodity_t::valid() const
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
commodity_t * commodity_t::create(const string& symbol)
|
|
||||||
{
|
|
||||||
std::auto_ptr<commodity_t> commodity(new commodity_t);
|
|
||||||
|
|
||||||
commodity->base = commodity_base_t::create(symbol);
|
|
||||||
|
|
||||||
if (needs_quotes(symbol)) {
|
|
||||||
commodity->qualified_symbol = "\"";
|
|
||||||
commodity->qualified_symbol += symbol;
|
|
||||||
commodity->qualified_symbol += "\"";
|
|
||||||
} else {
|
|
||||||
commodity->qualified_symbol = symbol;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEBUG("amounts.commodities",
|
|
||||||
"Creating commodity " << commodity->qualified_symbol);
|
|
||||||
|
|
||||||
std::pair<commodities_map::iterator, bool> result
|
|
||||||
= commodities.insert(commodities_pair(symbol, commodity.get()));
|
|
||||||
if (! result.second)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
commodity->ident = commodities_by_ident->size();
|
|
||||||
commodities_by_ident->push_back(commodity.get());
|
|
||||||
|
|
||||||
// Start out the new commodity with the default commodity's flags
|
|
||||||
// and precision, if one has been defined.
|
|
||||||
if (default_commodity)
|
|
||||||
commodity->drop_flags(COMMODITY_STYLE_THOUSANDS |
|
|
||||||
COMMODITY_STYLE_NOMARKET);
|
|
||||||
|
|
||||||
return commodity.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
commodity_t * commodity_t::find_or_create(const string& symbol)
|
|
||||||
{
|
|
||||||
DEBUG("amounts.commodities", "Find-or-create commodity " << symbol);
|
|
||||||
|
|
||||||
commodity_t * commodity = find(symbol);
|
|
||||||
if (commodity)
|
|
||||||
return commodity;
|
|
||||||
return create(symbol);
|
|
||||||
}
|
|
||||||
|
|
||||||
commodity_t * commodity_t::find(const string& symbol)
|
|
||||||
{
|
|
||||||
DEBUG("amounts.commodities", "Find commodity " << symbol);
|
|
||||||
|
|
||||||
commodities_map::const_iterator i = commodities.find(symbol);
|
|
||||||
if (i != commodities.end())
|
|
||||||
return (*i).second;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
amount_t commodity_base_t::value(const moment_t& moment)
|
|
||||||
{
|
|
||||||
moment_t age;
|
|
||||||
amount_t price;
|
|
||||||
|
|
||||||
if (history) {
|
|
||||||
assert(history->prices.size() > 0);
|
|
||||||
|
|
||||||
if (! is_valid_moment(moment)) {
|
|
||||||
history_map::reverse_iterator r = history->prices.rbegin();
|
|
||||||
age = (*r).first;
|
|
||||||
price = (*r).second;
|
|
||||||
} else {
|
|
||||||
history_map::iterator i = history->prices.lower_bound(moment);
|
|
||||||
if (i == history->prices.end()) {
|
|
||||||
history_map::reverse_iterator r = history->prices.rbegin();
|
|
||||||
age = (*r).first;
|
|
||||||
price = (*r).second;
|
|
||||||
} else {
|
|
||||||
age = (*i).first;
|
|
||||||
if (moment != age) {
|
|
||||||
if (i != history->prices.begin()) {
|
|
||||||
--i;
|
|
||||||
age = (*i).first;
|
|
||||||
price = (*i).second;
|
|
||||||
} else {
|
|
||||||
age = moment_t();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
price = (*i).second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (updater && ! (flags & COMMODITY_STYLE_NOMARKET))
|
|
||||||
(*updater)(*this, moment, age,
|
|
||||||
(history && history->prices.size() > 0 ?
|
|
||||||
(*history->prices.rbegin()).first : moment_t()), price);
|
|
||||||
|
|
||||||
return price;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool annotated_commodity_t::operator==(const commodity_t& comm) const
|
bool annotated_commodity_t::operator==(const commodity_t& comm) const
|
||||||
{
|
{
|
||||||
// If the base commodities don't match, the game's up.
|
// If the base commodities don't match, the game's up.
|
||||||
if (base != comm.base)
|
if (base != comm.base)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (price &&
|
assert(annotated);
|
||||||
(! comm.annotated ||
|
if (! comm.annotated)
|
||||||
price != static_cast<const annotated_commodity_t&>(comm).price))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (date &&
|
if (details != comm.as_annotated().details)
|
||||||
(! comm.annotated ||
|
|
||||||
date != static_cast<const annotated_commodity_t&>(comm).date))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (tag &&
|
|
||||||
(! comm.annotated ||
|
|
||||||
tag != static_cast<const annotated_commodity_t&>(comm).tag))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
annotated_commodity_t::write_annotations(std::ostream& out,
|
annotated_commodity_t::write_annotations(std::ostream& out,
|
||||||
const optional<amount_t>& price,
|
const annotation_t& info)
|
||||||
const optional<moment_t>& date,
|
|
||||||
const optional<string>& tag)
|
|
||||||
{
|
{
|
||||||
if (price)
|
if (info.price)
|
||||||
out << " {" << *price << '}';
|
out << " {" << *info.price << '}';
|
||||||
|
|
||||||
if (date)
|
if (info.date)
|
||||||
out << " [" << *date << ']';
|
out << " [" << *info.date << ']';
|
||||||
|
|
||||||
if (tag)
|
if (info.tag)
|
||||||
out << " (" << *tag << ')';
|
out << " (" << *info.tag << ')';
|
||||||
}
|
|
||||||
|
|
||||||
commodity_t *
|
|
||||||
annotated_commodity_t::create(const commodity_t& comm,
|
|
||||||
const optional<amount_t>& price,
|
|
||||||
const optional<moment_t>& date,
|
|
||||||
const optional<string>& tag,
|
|
||||||
const string& mapping_key)
|
|
||||||
{
|
|
||||||
std::auto_ptr<annotated_commodity_t> commodity(new annotated_commodity_t);
|
|
||||||
|
|
||||||
// Set the annotated bits
|
|
||||||
commodity->price = price;
|
|
||||||
commodity->date = date;
|
|
||||||
commodity->tag = tag;
|
|
||||||
|
|
||||||
commodity->ptr = &comm;
|
|
||||||
assert(commodity->ptr);
|
|
||||||
commodity->base = comm.base;
|
|
||||||
assert(commodity->base);
|
|
||||||
|
|
||||||
commodity->qualified_symbol = comm.symbol();
|
|
||||||
|
|
||||||
DEBUG("amounts.commodities", "Creating annotated commodity "
|
|
||||||
<< "symbol " << commodity->symbol()
|
|
||||||
<< " key " << mapping_key << std::endl
|
|
||||||
<< " price " << (price ? price->to_string() : "NONE") << " "
|
|
||||||
<< " date " << (date ? *date : moment_t()) << " "
|
|
||||||
<< " tag " << (tag ? *tag : "NONE"));
|
|
||||||
|
|
||||||
// Add the fully annotated name to the map, so that this symbol may
|
|
||||||
// quickly be found again.
|
|
||||||
std::pair<commodities_map::iterator, bool> result
|
|
||||||
= commodities.insert(commodities_pair(mapping_key, commodity.get()));
|
|
||||||
if (! result.second)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
commodity->ident = commodities_by_ident->size();
|
|
||||||
commodities_by_ident->push_back(commodity.get());
|
|
||||||
|
|
||||||
return commodity.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
string make_qualified_name(const commodity_t& comm,
|
|
||||||
const optional<amount_t>& price,
|
|
||||||
const optional<moment_t>& date,
|
|
||||||
const optional<string>& tag)
|
|
||||||
{
|
|
||||||
if (price && price->sign() < 0)
|
|
||||||
throw_(amount_error, "A commodity's price may not be negative");
|
|
||||||
|
|
||||||
std::ostringstream name;
|
|
||||||
|
|
||||||
comm.write(name);
|
|
||||||
annotated_commodity_t::write_annotations(name, price, date, tag);
|
|
||||||
|
|
||||||
DEBUG("amounts.commodities", "make_qualified_name for "
|
|
||||||
<< comm.qualified_symbol << std::endl
|
|
||||||
<< " price " << (price ? price->to_string() : "NONE") << " "
|
|
||||||
<< " date " << (date ? *date : moment_t()) << " "
|
|
||||||
<< " tag " << (tag ? *tag : "NONE"));
|
|
||||||
|
|
||||||
DEBUG("amounts.commodities", "qualified_name is " << name.str());
|
|
||||||
|
|
||||||
return name.str();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
commodity_t *
|
|
||||||
annotated_commodity_t::find_or_create(const commodity_t& comm,
|
|
||||||
const optional<amount_t>& price,
|
|
||||||
const optional<moment_t>& date,
|
|
||||||
const optional<string>& tag)
|
|
||||||
{
|
|
||||||
string name = make_qualified_name(comm, price, date, tag);
|
|
||||||
|
|
||||||
commodity_t * ann_comm = commodity_t::find(name);
|
|
||||||
if (ann_comm) {
|
|
||||||
assert(ann_comm->annotated);
|
|
||||||
return ann_comm;
|
|
||||||
}
|
|
||||||
return create(comm, price, date, tag, name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool compare_amount_commodities::operator()(const amount_t * left,
|
bool compare_amount_commodities::operator()(const amount_t * left,
|
||||||
|
|
@ -369,15 +216,15 @@ bool compare_amount_commodities::operator()(const amount_t * left,
|
||||||
annotated_commodity_t& aleftcomm(static_cast<annotated_commodity_t&>(leftcomm));
|
annotated_commodity_t& aleftcomm(static_cast<annotated_commodity_t&>(leftcomm));
|
||||||
annotated_commodity_t& arightcomm(static_cast<annotated_commodity_t&>(rightcomm));
|
annotated_commodity_t& arightcomm(static_cast<annotated_commodity_t&>(rightcomm));
|
||||||
|
|
||||||
if (! aleftcomm.price && arightcomm.price)
|
if (! aleftcomm.details.price && arightcomm.details.price)
|
||||||
return true;
|
return true;
|
||||||
if (aleftcomm.price && ! arightcomm.price)
|
if (aleftcomm.details.price && ! arightcomm.details.price)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (aleftcomm.price && arightcomm.price) {
|
if (aleftcomm.details.price && arightcomm.details.price) {
|
||||||
amount_t leftprice(*aleftcomm.price);
|
amount_t leftprice(*aleftcomm.details.price);
|
||||||
leftprice.in_place_reduce();
|
leftprice.in_place_reduce();
|
||||||
amount_t rightprice(*arightcomm.price);
|
amount_t rightprice(*arightcomm.details.price);
|
||||||
rightprice.in_place_reduce();
|
rightprice.in_place_reduce();
|
||||||
|
|
||||||
if (leftprice.commodity() == rightprice.commodity()) {
|
if (leftprice.commodity() == rightprice.commodity()) {
|
||||||
|
|
@ -392,27 +239,239 @@ bool compare_amount_commodities::operator()(const amount_t * left,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! aleftcomm.date && arightcomm.date)
|
if (! aleftcomm.details.date && arightcomm.details.date)
|
||||||
return true;
|
return true;
|
||||||
if (aleftcomm.date && ! arightcomm.date)
|
if (aleftcomm.details.date && ! arightcomm.details.date)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (aleftcomm.date && arightcomm.date) {
|
if (aleftcomm.details.date && arightcomm.details.date) {
|
||||||
duration_t diff = *aleftcomm.date - *arightcomm.date;
|
duration_t diff = *aleftcomm.details.date - *arightcomm.details.date;
|
||||||
return diff.is_negative();
|
return diff.is_negative();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! aleftcomm.tag && arightcomm.tag)
|
if (! aleftcomm.details.tag && arightcomm.details.tag)
|
||||||
return true;
|
return true;
|
||||||
if (aleftcomm.tag && ! arightcomm.tag)
|
if (aleftcomm.details.tag && ! arightcomm.details.tag)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (aleftcomm.tag && arightcomm.tag)
|
if (aleftcomm.details.tag && arightcomm.details.tag)
|
||||||
return *aleftcomm.tag < *arightcomm.tag;
|
return *aleftcomm.details.tag < *arightcomm.details.tag;
|
||||||
|
|
||||||
assert(false);
|
assert(false);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
commodity_pool_t::commodity_pool_t() : default_commodity(NULL)
|
||||||
|
{
|
||||||
|
null_commodity = create("");
|
||||||
|
null_commodity->add_flags(COMMODITY_STYLE_NOMARKET |
|
||||||
|
COMMODITY_STYLE_BUILTIN);
|
||||||
|
|
||||||
|
// Add time commodity conversions, so that timelog's may be parsed
|
||||||
|
// in terms of seconds, but reported as minutes or hours.
|
||||||
|
commodity_t * commodity = create("s");
|
||||||
|
commodity->add_flags(COMMODITY_STYLE_NOMARKET | COMMODITY_STYLE_BUILTIN);
|
||||||
|
|
||||||
|
amount_t::parse_conversion(*this, "1.0m", "60s");
|
||||||
|
amount_t::parse_conversion(*this, "1.0h", "60m");
|
||||||
|
}
|
||||||
|
|
||||||
|
commodity_t * commodity_pool_t::create(const string& symbol)
|
||||||
|
{
|
||||||
|
shared_ptr<commodity_base_t> base_commodity(new commodity_base_t(symbol));
|
||||||
|
std::auto_ptr<commodity_t> commodity(new commodity_t(this, base_commodity));
|
||||||
|
|
||||||
|
DEBUG("amounts.commodities", "Creating base commodity " << symbol);
|
||||||
|
|
||||||
|
// Create the "qualified symbol" version of this commodity's symbol
|
||||||
|
if (commodity_t::symbol_needs_quotes(symbol)) {
|
||||||
|
commodity->qualified_symbol = "\"";
|
||||||
|
*commodity->qualified_symbol += symbol;
|
||||||
|
*commodity->qualified_symbol += "\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG("amounts.commodities",
|
||||||
|
"Creating commodity '" << commodity->symbol() << "'");
|
||||||
|
|
||||||
|
// Start out the new commodity with the default commodity's flags
|
||||||
|
// and precision, if one has been defined.
|
||||||
|
#if 0
|
||||||
|
// jww (2007-05-02): This doesn't do anything currently!
|
||||||
|
if (default_commodity)
|
||||||
|
commodity->drop_flags(COMMODITY_STYLE_THOUSANDS |
|
||||||
|
COMMODITY_STYLE_NOMARKET);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
commodity->ident = commodities.size();
|
||||||
|
|
||||||
|
std::pair<commodities_t::iterator, bool> result =
|
||||||
|
commodities.insert(commodity.get());
|
||||||
|
if (! result.second) {
|
||||||
|
assert(false);
|
||||||
|
return NULL;
|
||||||
|
} else {
|
||||||
|
return commodity.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
commodity_t * commodity_pool_t::find_or_create(const string& symbol)
|
||||||
|
{
|
||||||
|
DEBUG("amounts.commodities", "Find-or-create commodity " << symbol);
|
||||||
|
|
||||||
|
commodity_t * commodity = find(symbol);
|
||||||
|
if (commodity)
|
||||||
|
return commodity;
|
||||||
|
return create(symbol);
|
||||||
|
}
|
||||||
|
|
||||||
|
commodity_t * commodity_pool_t::find(const string& symbol)
|
||||||
|
{
|
||||||
|
DEBUG("amounts.commodities", "Find commodity " << symbol);
|
||||||
|
|
||||||
|
typedef commodity_pool_t::commodities_t::nth_index<1>::type
|
||||||
|
commodities_by_name;
|
||||||
|
|
||||||
|
commodities_by_name& name_index = commodities.get<1>();
|
||||||
|
commodities_by_name::const_iterator i = name_index.find(symbol);
|
||||||
|
if (i != name_index.end())
|
||||||
|
return *i;
|
||||||
|
else
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
commodity_t * commodity_pool_t::find(const commodity_t::ident_t ident)
|
||||||
|
{
|
||||||
|
DEBUG("amounts.commodities", "Find commodity by ident " << ident);
|
||||||
|
|
||||||
|
typedef commodity_pool_t::commodities_t::nth_index<0>::type
|
||||||
|
commodities_by_ident;
|
||||||
|
|
||||||
|
commodities_by_ident& ident_index = commodities.get<0>();
|
||||||
|
commodities_by_ident::iterator i = ident_index.find(ident);
|
||||||
|
if (i != ident_index.end())
|
||||||
|
return *i;
|
||||||
|
else
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
commodity_t *
|
||||||
|
commodity_pool_t::create(const string& symbol, const annotation_t& details)
|
||||||
|
{
|
||||||
|
commodity_t * new_comm = create(symbol);
|
||||||
|
if (! new_comm)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (details)
|
||||||
|
return find_or_create(*new_comm, details);
|
||||||
|
else
|
||||||
|
return new_comm;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
string make_qualified_name(const commodity_t& comm,
|
||||||
|
const annotation_t& details)
|
||||||
|
{
|
||||||
|
assert(details);
|
||||||
|
|
||||||
|
if (details.price && details.price->sign() < 0)
|
||||||
|
throw_(amount_error, "A commodity's price may not be negative");
|
||||||
|
|
||||||
|
std::ostringstream name;
|
||||||
|
comm.write(name);
|
||||||
|
annotated_commodity_t::write_annotations(name, details);
|
||||||
|
|
||||||
|
DEBUG("amounts.commodities", "make_qualified_name for "
|
||||||
|
<< comm.qualified_symbol << std::endl << details);
|
||||||
|
DEBUG("amounts.commodities", "qualified_name is " << name.str());
|
||||||
|
|
||||||
|
return name.str();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
commodity_t *
|
||||||
|
commodity_pool_t::find(const string& symbol, const annotation_t& details)
|
||||||
|
{
|
||||||
|
commodity_t * comm = find(symbol);
|
||||||
|
if (! comm)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (details) {
|
||||||
|
string name = make_qualified_name(*comm, details);
|
||||||
|
|
||||||
|
if (commodity_t * ann_comm = find(name)) {
|
||||||
|
assert(ann_comm->annotated &&
|
||||||
|
ann_comm->as_annotated().details);
|
||||||
|
return ann_comm;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
} else {
|
||||||
|
return comm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
commodity_t *
|
||||||
|
commodity_pool_t::find_or_create(const string& symbol,
|
||||||
|
const annotation_t& details)
|
||||||
|
{
|
||||||
|
commodity_t * comm = find(symbol);
|
||||||
|
if (! comm)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (details)
|
||||||
|
return find_or_create(*comm, details);
|
||||||
|
else
|
||||||
|
return comm;
|
||||||
|
}
|
||||||
|
|
||||||
|
commodity_t *
|
||||||
|
commodity_pool_t::create(commodity_t& comm,
|
||||||
|
const annotation_t& details,
|
||||||
|
const string& mapping_key)
|
||||||
|
{
|
||||||
|
assert(comm);
|
||||||
|
assert(details);
|
||||||
|
assert(! mapping_key.empty());
|
||||||
|
|
||||||
|
std::auto_ptr<commodity_t> commodity
|
||||||
|
(new annotated_commodity_t(&comm, details));
|
||||||
|
|
||||||
|
commodity->qualified_symbol = comm.symbol();
|
||||||
|
assert(! commodity->qualified_symbol->empty());
|
||||||
|
|
||||||
|
DEBUG("amounts.commodities", "Creating annotated commodity "
|
||||||
|
<< "symbol " << commodity->symbol()
|
||||||
|
<< " key " << mapping_key << std::endl << details);
|
||||||
|
|
||||||
|
// Add the fully annotated name to the map, so that this symbol may
|
||||||
|
// quickly be found again.
|
||||||
|
commodity->ident = commodities.size();
|
||||||
|
commodity->mapping_key_ = mapping_key;
|
||||||
|
|
||||||
|
std::pair<commodities_t::iterator, bool> result
|
||||||
|
= commodities.insert(commodity.get());
|
||||||
|
if (! result.second) {
|
||||||
|
assert(false);
|
||||||
|
return NULL;
|
||||||
|
} else {
|
||||||
|
return commodity.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
commodity_t * commodity_pool_t::find_or_create(commodity_t& comm,
|
||||||
|
const annotation_t& details)
|
||||||
|
{
|
||||||
|
assert(comm);
|
||||||
|
assert(details);
|
||||||
|
|
||||||
|
string name = make_qualified_name(comm, details);
|
||||||
|
assert(! name.empty());
|
||||||
|
|
||||||
|
if (commodity_t * ann_comm = find(name)) {
|
||||||
|
assert(ann_comm->annotated && ann_comm->as_annotated().details);
|
||||||
|
return ann_comm;
|
||||||
|
}
|
||||||
|
return create(comm, details, name);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ledger
|
} // namespace ledger
|
||||||
|
|
|
||||||
405
src/commodity.h
405
src/commodity.h
|
|
@ -53,232 +53,229 @@ namespace ledger {
|
||||||
#define COMMODITY_STYLE_NOMARKET 0x0010
|
#define COMMODITY_STYLE_NOMARKET 0x0010
|
||||||
#define COMMODITY_STYLE_BUILTIN 0x0020
|
#define COMMODITY_STYLE_BUILTIN 0x0020
|
||||||
|
|
||||||
typedef std::map<const moment_t, amount_t> history_map;
|
|
||||||
typedef std::pair<const moment_t, amount_t> history_pair;
|
|
||||||
|
|
||||||
class commodity_base_t;
|
|
||||||
|
|
||||||
typedef std::map<const string, commodity_base_t *> base_commodities_map;
|
|
||||||
typedef std::pair<const string, commodity_base_t *> base_commodities_pair;
|
|
||||||
|
|
||||||
class commodity_base_t : public noncopyable
|
class commodity_base_t : public noncopyable
|
||||||
{
|
{
|
||||||
public:
|
private:
|
||||||
|
friend class commodity_pool_t;
|
||||||
friend class commodity_t;
|
friend class commodity_t;
|
||||||
friend class annotated_commodity_t;
|
friend class annotated_commodity_t;
|
||||||
|
|
||||||
friend void amount_t::initialize();
|
typedef std::map<const moment_t, amount_t> history_map;
|
||||||
friend void amount_t::shutdown();
|
typedef std::pair<const moment_t, amount_t> history_pair;
|
||||||
|
|
||||||
friend void checked_delete<commodity_base_t>(commodity_base_t *);
|
|
||||||
|
|
||||||
typedef uint_least32_t ident_t;
|
|
||||||
|
|
||||||
ident_t ident;
|
|
||||||
string name;
|
|
||||||
string note;
|
|
||||||
amount_t::precision_t precision;
|
|
||||||
unsigned char flags;
|
|
||||||
optional<amount_t> smaller;
|
|
||||||
optional<amount_t> larger;
|
|
||||||
|
|
||||||
commodity_base_t() : precision(0), flags(COMMODITY_STYLE_DEFAULTS) {
|
|
||||||
TRACE_CTOR(commodity_base_t, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
commodity_base_t(const commodity_base_t&) {
|
|
||||||
TRACE_CTOR(commodity_base_t, "copy");
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
commodity_base_t(const string& _symbol,
|
|
||||||
unsigned int _precision = 0,
|
|
||||||
unsigned int _flags = COMMODITY_STYLE_DEFAULTS)
|
|
||||||
: precision(_precision), flags(_flags), symbol(_symbol) {
|
|
||||||
TRACE_CTOR(commodity_base_t, "const string&, unsigned int, unsigned int");
|
|
||||||
}
|
|
||||||
|
|
||||||
~commodity_base_t() {
|
|
||||||
TRACE_DTOR(commodity_base_t);
|
|
||||||
}
|
|
||||||
|
|
||||||
static base_commodities_map commodities;
|
|
||||||
|
|
||||||
static commodity_base_t * create(const string& symbol);
|
|
||||||
|
|
||||||
string symbol;
|
|
||||||
|
|
||||||
struct history_t {
|
struct history_t {
|
||||||
history_map prices;
|
history_map prices;
|
||||||
ptime last_lookup;
|
ptime last_lookup;
|
||||||
history_t() : last_lookup() {}
|
|
||||||
};
|
};
|
||||||
optional<history_t> history;
|
|
||||||
|
|
||||||
void add_price(const moment_t& date, const amount_t& price);
|
typedef uint_least8_t flags_t;
|
||||||
bool remove_price(const moment_t& date);
|
|
||||||
amount_t value(const moment_t& moment = now);
|
flags_t flags;
|
||||||
|
string symbol;
|
||||||
|
amount_t::precision_t precision;
|
||||||
|
optional<string> name;
|
||||||
|
optional<string> note;
|
||||||
|
optional<history_t> history;
|
||||||
|
optional<amount_t> smaller;
|
||||||
|
optional<amount_t> larger;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
class updater_t {
|
explicit commodity_base_t()
|
||||||
public:
|
: flags(COMMODITY_STYLE_DEFAULTS), precision(0) {
|
||||||
virtual ~updater_t() {}
|
TRACE_CTOR(commodity_base_t, "");
|
||||||
virtual void operator()(commodity_base_t& commodity,
|
}
|
||||||
const moment_t& moment,
|
explicit commodity_base_t
|
||||||
const moment_t& date,
|
(const string& _symbol,
|
||||||
const moment_t& last,
|
amount_t::precision_t _precision = 0,
|
||||||
amount_t& price) = 0;
|
unsigned int _flags = COMMODITY_STYLE_DEFAULTS)
|
||||||
};
|
: flags(_flags), symbol(_symbol), precision(_precision) {
|
||||||
friend class updater_t;
|
TRACE_CTOR(commodity_base_t,
|
||||||
|
"const string&, amount_t::precision_t, unsigned int");
|
||||||
static updater_t * updater;
|
}
|
||||||
|
~commodity_base_t() {
|
||||||
|
TRACE_DTOR(commodity_base_t);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::map<const string, commodity_t *> commodities_map;
|
class annotated_commodity_t;
|
||||||
typedef std::pair<const string, commodity_t *> commodities_pair;
|
|
||||||
|
|
||||||
typedef std::vector<commodity_t *> commodities_array;
|
|
||||||
|
|
||||||
class commodity_t
|
class commodity_t
|
||||||
: public equality_comparable<commodity_t, noncopyable>
|
: public equality_comparable1<commodity_t, noncopyable>
|
||||||
{
|
{
|
||||||
friend class annotated_commodity_t;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// This map remembers all commodities that have been defined.
|
static bool symbol_needs_quotes(const string& symbol);
|
||||||
|
|
||||||
static commodities_map commodities;
|
typedef commodity_base_t::flags_t flags_t;
|
||||||
static commodities_array * commodities_by_ident;
|
typedef commodity_base_t::history_t history_t;
|
||||||
static bool commodities_sorted;
|
typedef commodity_base_t::history_map history_map;
|
||||||
static commodity_t * null_commodity;
|
typedef commodity_base_t::history_pair history_pair;
|
||||||
static commodity_t * default_commodity;
|
typedef uint_least32_t ident_t;
|
||||||
|
|
||||||
static commodity_t * create(const string& symbol);
|
shared_ptr<commodity_base_t> base;
|
||||||
static commodity_t * find(const string& name);
|
|
||||||
static commodity_t * find_or_create(const string& symbol);
|
|
||||||
|
|
||||||
static bool needs_quotes(const string& symbol);
|
|
||||||
|
|
||||||
static void make_alias(const string& symbol,
|
|
||||||
commodity_t * commodity);
|
|
||||||
|
|
||||||
// These are specific to each commodity reference
|
|
||||||
|
|
||||||
typedef unsigned long ident_t;
|
|
||||||
|
|
||||||
|
commodity_pool_t * parent_;
|
||||||
ident_t ident;
|
ident_t ident;
|
||||||
commodity_base_t * base;
|
optional<string> qualified_symbol;
|
||||||
string qualified_symbol;
|
optional<string> mapping_key_;
|
||||||
bool annotated;
|
bool annotated;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit commodity_t() : base(NULL), annotated(false) {
|
explicit commodity_t(commodity_pool_t * _parent,
|
||||||
|
const shared_ptr<commodity_base_t>& _base)
|
||||||
|
: base(_base), parent_(_parent), annotated(false) {
|
||||||
TRACE_CTOR(commodity_t, "");
|
TRACE_CTOR(commodity_t, "");
|
||||||
}
|
}
|
||||||
commodity_t(const commodity_t& o)
|
|
||||||
: ident(o.ident), base(o.base),
|
|
||||||
qualified_symbol(o.qualified_symbol), annotated(o.annotated) {
|
|
||||||
TRACE_CTOR(commodity_t, "copy");
|
|
||||||
}
|
|
||||||
virtual ~commodity_t() {
|
virtual ~commodity_t() {
|
||||||
TRACE_DTOR(commodity_t);
|
TRACE_DTOR(commodity_t);
|
||||||
}
|
}
|
||||||
|
|
||||||
operator bool() const {
|
operator bool() const;
|
||||||
return this != null_commodity;
|
|
||||||
}
|
|
||||||
virtual bool operator==(const commodity_t& comm) const {
|
virtual bool operator==(const commodity_t& comm) const {
|
||||||
if (comm.annotated)
|
if (comm.annotated)
|
||||||
return comm == *this;
|
return comm == *this;
|
||||||
return base == comm.base;
|
return base.get() == comm.base.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
commodity_pool_t& parent() const {
|
||||||
|
return *parent_;
|
||||||
|
}
|
||||||
|
|
||||||
|
annotated_commodity_t& as_annotated();
|
||||||
|
const annotated_commodity_t& as_annotated() const;
|
||||||
|
|
||||||
string base_symbol() const {
|
string base_symbol() const {
|
||||||
return base->symbol;
|
return base->symbol;
|
||||||
}
|
}
|
||||||
string symbol() const {
|
string symbol() const {
|
||||||
return qualified_symbol;
|
return qualified_symbol ? *qualified_symbol : base_symbol();
|
||||||
}
|
}
|
||||||
|
|
||||||
void write(std::ostream& out) const {
|
string mapping_key() const {
|
||||||
out << symbol();
|
if (mapping_key_)
|
||||||
|
return *mapping_key_;
|
||||||
|
else
|
||||||
|
return base_symbol();
|
||||||
}
|
}
|
||||||
|
|
||||||
string name() const {
|
optional<string> name() const {
|
||||||
return base->name;
|
return base->name;
|
||||||
}
|
}
|
||||||
void set_name(const string& arg) {
|
void set_name(const optional<string>& arg = optional<string>()) {
|
||||||
base->name = arg;
|
base->name = arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
string note() const {
|
optional<string> note() const {
|
||||||
return base->note;
|
return base->note;
|
||||||
}
|
}
|
||||||
void set_note(const string& arg) {
|
void set_note(const optional<string>& arg = optional<string>()) {
|
||||||
base->note = arg;
|
base->note = arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char precision() const {
|
amount_t::precision_t precision() const {
|
||||||
return base->precision;
|
return base->precision;
|
||||||
}
|
}
|
||||||
void set_precision(unsigned char arg) {
|
void set_precision(amount_t::precision_t arg) {
|
||||||
base->precision = arg;
|
base->precision = arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char flags() const {
|
flags_t flags() const {
|
||||||
return base->flags;
|
return base->flags;
|
||||||
}
|
}
|
||||||
void set_flags(unsigned char arg) {
|
void set_flags(flags_t arg) {
|
||||||
base->flags = arg;
|
base->flags = arg;
|
||||||
}
|
}
|
||||||
void add_flags(unsigned char arg) {
|
void add_flags(flags_t arg) {
|
||||||
base->flags |= arg;
|
base->flags |= arg;
|
||||||
}
|
}
|
||||||
void drop_flags(unsigned char arg) {
|
void drop_flags(flags_t arg) {
|
||||||
base->flags &= ~arg;
|
base->flags &= ~arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
optional<amount_t> smaller() const {
|
optional<amount_t> smaller() const {
|
||||||
return base->smaller;
|
return base->smaller;
|
||||||
}
|
}
|
||||||
void set_smaller(const amount_t& arg) {
|
void set_smaller(const optional<amount_t>& arg = optional<amount_t>()) {
|
||||||
base->smaller = arg;
|
base->smaller = arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
optional<amount_t> larger() const {
|
optional<amount_t> larger() const {
|
||||||
return base->larger;
|
return base->larger;
|
||||||
}
|
}
|
||||||
void set_larger(const amount_t& arg) {
|
void set_larger(const optional<amount_t>& arg = optional<amount_t>()) {
|
||||||
base->larger = arg;
|
base->larger = arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
optional<commodity_base_t::history_t> history() const {
|
optional<history_t> history() const {
|
||||||
return base->history;
|
return base->history;
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_price(const moment_t& date, const amount_t& price) {
|
void add_price(const moment_t& date, const amount_t& price);
|
||||||
return base->add_price(date, price);
|
bool remove_price(const moment_t& date);
|
||||||
}
|
|
||||||
bool remove_price(const moment_t& date) {
|
optional<amount_t> value(const optional<moment_t>& moment =
|
||||||
return base->remove_price(date);
|
optional<moment_t>());
|
||||||
}
|
|
||||||
amount_t value(const moment_t& moment = now) const {
|
void write(std::ostream& out) const {
|
||||||
return base->value(moment);
|
out << symbol();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool valid() const;
|
bool valid() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class annotated_commodity_t : public commodity_t
|
inline std::ostream& operator<<(std::ostream& out, const commodity_t& comm) {
|
||||||
|
comm.write(out);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct annotation_t : public equality_comparable<annotation_t>
|
||||||
{
|
{
|
||||||
public:
|
optional<amount_t> price;
|
||||||
const commodity_t * ptr;
|
optional<moment_t> date;
|
||||||
|
optional<string> tag;
|
||||||
|
|
||||||
optional<amount_t> price;
|
explicit annotation_t(const optional<amount_t>& _price = optional<amount_t>(),
|
||||||
optional<moment_t> date;
|
const optional<moment_t>& _date = optional<moment_t>(),
|
||||||
optional<string> tag;
|
const optional<string>& _tag = optional<string>())
|
||||||
|
: price(_price), date(_date), tag(_tag) {}
|
||||||
|
|
||||||
explicit annotated_commodity_t() {
|
operator bool() const {
|
||||||
|
return price || date || tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const annotation_t& rhs) const {
|
||||||
|
return (price == rhs.price &&
|
||||||
|
date == rhs.date &&
|
||||||
|
tag == rhs.tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
void write(std::ostream& out) const {
|
||||||
|
out << "price " << (price ? price->to_string() : "NONE") << " "
|
||||||
|
<< "date " << (date ? *date : moment_t()) << " "
|
||||||
|
<< "tag " << (tag ? *tag : "NONE");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool valid() const {
|
||||||
|
assert(*this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
inline std::ostream& operator<<(std::ostream& out, const annotation_t& details) {
|
||||||
|
details.write(out);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
class annotated_commodity_t
|
||||||
|
: public commodity_t,
|
||||||
|
equality_comparable1<annotated_commodity_t, noncopyable>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
commodity_t * ptr;
|
||||||
|
annotation_t details;
|
||||||
|
|
||||||
|
explicit annotated_commodity_t(commodity_t * _ptr,
|
||||||
|
const annotation_t& _details)
|
||||||
|
: commodity_t(_ptr->parent_, _ptr->base), ptr(_ptr), details(_details) {
|
||||||
TRACE_CTOR(annotated_commodity_t, "");
|
TRACE_CTOR(annotated_commodity_t, "");
|
||||||
annotated = true;
|
annotated = true;
|
||||||
}
|
}
|
||||||
|
|
@ -288,39 +285,131 @@ class annotated_commodity_t : public commodity_t
|
||||||
|
|
||||||
virtual bool operator==(const commodity_t& comm) const;
|
virtual bool operator==(const commodity_t& comm) const;
|
||||||
|
|
||||||
void write_annotations(std::ostream& out) const {
|
commodity_t& referent() {
|
||||||
annotated_commodity_t::write_annotations(out, price, date, tag);
|
return *ptr;
|
||||||
|
}
|
||||||
|
const commodity_t& referent() const {
|
||||||
|
return *ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void write_annotations(std::ostream& out,
|
void write_annotations(std::ostream& out) const {
|
||||||
const optional<amount_t>& price,
|
annotated_commodity_t::write_annotations(out, details);
|
||||||
const optional<moment_t>& date,
|
}
|
||||||
const optional<string>& tag);
|
|
||||||
|
|
||||||
private:
|
static void write_annotations(std::ostream& out,
|
||||||
static commodity_t * create(const commodity_t& comm,
|
const annotation_t& info);
|
||||||
const optional<amount_t>& price,
|
|
||||||
const optional<moment_t>& date,
|
|
||||||
const optional<string>& tag,
|
|
||||||
const string& mapping_key);
|
|
||||||
|
|
||||||
static commodity_t * find_or_create(const commodity_t& comm,
|
|
||||||
const optional<amount_t>& price,
|
|
||||||
const optional<moment_t>& date,
|
|
||||||
const optional<string>& tag);
|
|
||||||
|
|
||||||
friend class amount_t;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
inline std::ostream& operator<<(std::ostream& out, const commodity_t& comm) {
|
|
||||||
out << comm.symbol();
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct compare_amount_commodities {
|
struct compare_amount_commodities {
|
||||||
bool operator()(const amount_t * left, const amount_t * right) const;
|
bool operator()(const amount_t * left, const amount_t * right) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class commodity_pool_t : public noncopyable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* The commodities collection in commodity_pool_t maintains pointers
|
||||||
|
* to all the commodities which have ever been created by the user,
|
||||||
|
* whether explicitly by calling the create methods of
|
||||||
|
* commodity_pool_t, or implicitly by parsing a commoditized amount.
|
||||||
|
*
|
||||||
|
* The `commodities' member variable represents a collection which
|
||||||
|
* is indexed by two vertices: first, and ordered sequence of unique
|
||||||
|
* integer which identify commodities by a numerical identifier; and
|
||||||
|
* second, by a hashed set of symbolic names which reflect how the
|
||||||
|
* commodity was referred to by the user.
|
||||||
|
*/
|
||||||
|
typedef multi_index_container<
|
||||||
|
commodity_t *,
|
||||||
|
multi_index::indexed_by<
|
||||||
|
multi_index::ordered_unique<
|
||||||
|
multi_index::member<commodity_t,
|
||||||
|
commodity_t::ident_t, &commodity_t::ident> >,
|
||||||
|
multi_index::hashed_unique<
|
||||||
|
multi_index::const_mem_fun<commodity_t,
|
||||||
|
string, &commodity_t::mapping_key> >
|
||||||
|
>
|
||||||
|
> commodities_t;
|
||||||
|
|
||||||
|
commodities_t commodities;
|
||||||
|
commodity_t * null_commodity;
|
||||||
|
commodity_t * default_commodity;
|
||||||
|
|
||||||
|
private:
|
||||||
|
template<typename T>
|
||||||
|
struct first_initialized
|
||||||
|
{
|
||||||
|
typedef T result_type;
|
||||||
|
|
||||||
|
template<typename InputIterator>
|
||||||
|
T operator()(InputIterator first, InputIterator last) const
|
||||||
|
{
|
||||||
|
for (; first != last; first++)
|
||||||
|
if (*first)
|
||||||
|
return *first;
|
||||||
|
return T();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
boost::signal<optional<amount_t>
|
||||||
|
(commodity_t& commodity,
|
||||||
|
const optional<moment_t>& date,
|
||||||
|
const optional<moment_t>& moment,
|
||||||
|
const optional<moment_t>& last),
|
||||||
|
first_initialized<optional<amount_t> > > get_quote;
|
||||||
|
|
||||||
|
explicit commodity_pool_t();
|
||||||
|
|
||||||
|
~commodity_pool_t() {
|
||||||
|
typedef commodity_pool_t::commodities_t::nth_index<0>::type
|
||||||
|
commodities_by_ident;
|
||||||
|
|
||||||
|
commodities_by_ident& ident_index = commodities.get<0>();
|
||||||
|
for (commodities_by_ident::iterator i = ident_index.begin();
|
||||||
|
i != ident_index.end();
|
||||||
|
i++)
|
||||||
|
checked_delete(*i);
|
||||||
|
}
|
||||||
|
|
||||||
|
commodity_t * create(const string& symbol);
|
||||||
|
commodity_t * find(const string& name);
|
||||||
|
commodity_t * find(const commodity_t::ident_t ident);
|
||||||
|
commodity_t * find_or_create(const string& symbol);
|
||||||
|
|
||||||
|
commodity_t * create(const string& symbol, const annotation_t& details);
|
||||||
|
commodity_t * find(const string& symbol, const annotation_t& details);
|
||||||
|
commodity_t * find_or_create(const string& symbol,
|
||||||
|
const annotation_t& details);
|
||||||
|
|
||||||
|
commodity_t * create(commodity_t& comm,
|
||||||
|
const annotation_t& details,
|
||||||
|
const string& mapping_key);
|
||||||
|
|
||||||
|
commodity_t * find_or_create(commodity_t& comm,
|
||||||
|
const annotation_t& details);
|
||||||
|
|
||||||
|
void parse_amount(amount_t& amount, std::istream& in,
|
||||||
|
amount_t::flags_t flags = 0) {
|
||||||
|
amount.parse(*this, in, flags);
|
||||||
|
}
|
||||||
|
void parse_amount(amount_t& amount, const string& str,
|
||||||
|
amount_t::flags_t flags = 0) {
|
||||||
|
amount.parse(*this, str, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
amount_t parse_amount(std::istream& in, amount_t::flags_t flags = 0) {
|
||||||
|
amount_t temp;
|
||||||
|
parse_amount(temp, in, flags);
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
amount_t parse_amount(const string& str, amount_t::flags_t flags = 0) {
|
||||||
|
amount_t temp;
|
||||||
|
parse_amount(temp, str, flags);
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace ledger
|
} // namespace ledger
|
||||||
|
|
||||||
#endif // _COMMODITY_H
|
#endif // _COMMODITY_H
|
||||||
|
|
|
||||||
|
|
@ -191,7 +191,7 @@ void dataHandler(void *userData, const char *s, int len)
|
||||||
string symbol(s, len);
|
string symbol(s, len);
|
||||||
if (symbol == "USD") symbol = "$";
|
if (symbol == "USD") symbol = "$";
|
||||||
|
|
||||||
parser->curr_comm = commodity_t::find_or_create(symbol);
|
parser->curr_comm = amount_t::default_pool->find_or_create(symbol);
|
||||||
assert(parser->curr_comm);
|
assert(parser->curr_comm);
|
||||||
|
|
||||||
if (symbol != "$")
|
if (symbol != "$")
|
||||||
|
|
@ -320,7 +320,7 @@ unsigned int gnucash_parser_t::parse(std::istream& in,
|
||||||
|
|
||||||
// GnuCash uses the USD commodity without defining it, which really
|
// GnuCash uses the USD commodity without defining it, which really
|
||||||
// means $.
|
// means $.
|
||||||
commodity_t * usd = commodity_t::find_or_create("$");
|
commodity_t * usd = amount_t::default_pool->find_or_create("$");
|
||||||
usd->set_precision(2);
|
usd->set_precision(2);
|
||||||
usd->add_flags(COMMODITY_STYLE_THOUSANDS);
|
usd->add_flags(COMMODITY_STYLE_THOUSANDS);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -115,8 +115,9 @@ bool entry_base_t::finalize()
|
||||||
annotated_commodity_t&
|
annotated_commodity_t&
|
||||||
ann_comm(static_cast<annotated_commodity_t&>
|
ann_comm(static_cast<annotated_commodity_t&>
|
||||||
((*x)->amount->commodity()));
|
((*x)->amount->commodity()));
|
||||||
if (ann_comm.price)
|
if (ann_comm.details.price)
|
||||||
balance += *ann_comm.price * (*x)->amount->number() - *((*x)->cost);
|
balance += (*ann_comm.details.price * (*x)->amount->number() -
|
||||||
|
*((*x)->cost));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
saw_null = true;
|
saw_null = true;
|
||||||
|
|
@ -173,9 +174,9 @@ bool entry_base_t::finalize()
|
||||||
if ((*x)->amount->commodity() &&
|
if ((*x)->amount->commodity() &&
|
||||||
! (*x)->amount->commodity().annotated)
|
! (*x)->amount->commodity().annotated)
|
||||||
(*x)->amount->annotate_commodity
|
(*x)->amount->annotate_commodity
|
||||||
(per_unit_cost.abs(),
|
(annotation_t(per_unit_cost.abs(),
|
||||||
entry ? entry->actual_date() : optional<moment_t>(),
|
entry ? entry->actual_date() : optional<moment_t>(),
|
||||||
entry ? entry->code : optional<string>());
|
entry ? entry->code : optional<string>()));
|
||||||
|
|
||||||
(*x)->cost = - (per_unit_cost * (*x)->amount->number());
|
(*x)->cost = - (per_unit_cost * (*x)->amount->number());
|
||||||
balance += *(*x)->cost;
|
balance += *(*x)->cost;
|
||||||
|
|
@ -610,14 +611,6 @@ bool journal_t::valid() const
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (commodities_map::const_iterator i = commodity_t::commodities.begin();
|
|
||||||
i != commodity_t::commodities.end();
|
|
||||||
i++)
|
|
||||||
if (! (*i).second->valid()) {
|
|
||||||
DEBUG("ledger.validate", "journal_t: commodity not valid");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
24
src/main.cc
24
src/main.cc
|
|
@ -408,26 +408,26 @@ int main(int argc, char * argv[], char * envp[])
|
||||||
#if defined(TRACING_ON)
|
#if defined(TRACING_ON)
|
||||||
if (i + 1 < argc && std::strcmp(argv[i], "--trace") == 0) {
|
if (i + 1 < argc && std::strcmp(argv[i], "--trace") == 0) {
|
||||||
ledger::_log_level = LOG_TRACE;
|
ledger::_log_level = LOG_TRACE;
|
||||||
ledger::_trace_level = lexical_cast<int>(argv[i + 1]);
|
ledger::_trace_level = std::atoi(argv[i + 1]);
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IF_VERIFY()
|
||||||
|
initialize_memory_tracing();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
std::ios::sync_with_stdio(false);
|
std::ios::sync_with_stdio(false);
|
||||||
boost::filesystem::path::default_name_check
|
boost::filesystem::path::default_name_check
|
||||||
(boost::filesystem::portable_posix_name);
|
(boost::filesystem::portable_posix_name);
|
||||||
|
|
||||||
ledger::initialize();
|
|
||||||
|
|
||||||
#if ! defined(FULL_DEBUG)
|
|
||||||
ledger::do_cleanup = false;
|
|
||||||
#endif
|
|
||||||
INFO("Ledger starting");
|
INFO("Ledger starting");
|
||||||
|
|
||||||
std::auto_ptr<ledger::session_t> session(new ledger::session_t);
|
std::auto_ptr<ledger::session_t> session(new ledger::session_t);
|
||||||
|
|
||||||
|
ledger::set_session_context(session.get());
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
session->register_parser(new binary_parser_t);
|
session->register_parser(new binary_parser_t);
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -445,9 +445,11 @@ int main(int argc, char * argv[], char * envp[])
|
||||||
|
|
||||||
status = read_and_report(report.get(), argc, argv, envp);
|
status = read_and_report(report.get(), argc, argv, envp);
|
||||||
|
|
||||||
IF_VERIFY() {
|
if (! DO_VERIFY()) {
|
||||||
report.release();
|
report.release();
|
||||||
session.release();
|
session.release();
|
||||||
|
} else {
|
||||||
|
ledger::set_session_context();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#if 0
|
#if 0
|
||||||
|
|
@ -480,8 +482,12 @@ int main(int argc, char * argv[], char * envp[])
|
||||||
status = _status;
|
status = _status;
|
||||||
}
|
}
|
||||||
|
|
||||||
IF_VERIFY()
|
IF_VERIFY() {
|
||||||
ledger::shutdown();
|
INFO("Ledger ended (Boost/libstdc++ may still hold memory)");
|
||||||
|
shutdown_memory_tracing();
|
||||||
|
} else {
|
||||||
|
INFO("Ledger ended");
|
||||||
|
}
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,7 @@ unsigned int qif_parser_t::parse(std::istream& in,
|
||||||
unsigned char prec = xact->amount->commodity().precision();
|
unsigned char prec = xact->amount->commodity().precision();
|
||||||
|
|
||||||
if (! def_commodity) {
|
if (! def_commodity) {
|
||||||
def_commodity = commodity_t::find_or_create("$");
|
def_commodity = amount_t::default_pool->find_or_create("$");
|
||||||
assert(def_commodity);
|
assert(def_commodity);
|
||||||
}
|
}
|
||||||
xact->amount->set_commodity(*def_commodity);
|
xact->amount->set_commodity(*def_commodity);
|
||||||
|
|
|
||||||
|
|
@ -2,31 +2,36 @@
|
||||||
|
|
||||||
namespace ledger {
|
namespace ledger {
|
||||||
|
|
||||||
void quotes_by_script::operator()(commodity_base_t& commodity,
|
optional<amount_t>
|
||||||
const ptime& moment,
|
quotes_by_script::operator()(commodity_t& commodity,
|
||||||
const ptime& date,
|
const optional<moment_t>& date,
|
||||||
const ptime& last,
|
const optional<moment_t>& moment,
|
||||||
amount_t& price)
|
const optional<moment_t>& last)
|
||||||
{
|
{
|
||||||
LOGGER("quotes.download");
|
LOGGER("quotes.download");
|
||||||
|
|
||||||
DEBUG_("commodity: " << commodity.symbol);
|
IF_DEBUG_() {
|
||||||
DEBUG_(" now: " << now);
|
DEBUG_("commodity: " << commodity.symbol());
|
||||||
DEBUG_(" moment: " << moment);
|
DEBUG_(" now: " << now);
|
||||||
DEBUG_(" date: " << date);
|
if (date)
|
||||||
DEBUG_(" last: " << last);
|
DEBUG_(" date: " << date);
|
||||||
|
if (moment)
|
||||||
if (SHOW_DEBUG_() && commodity.history)
|
DEBUG_(" moment: " << moment);
|
||||||
DEBUG_("last_lookup: " << commodity.history->last_lookup);
|
if (last)
|
||||||
|
DEBUG_(" last: " << last);
|
||||||
|
if (commodity.history())
|
||||||
|
DEBUG_("last_lookup: " << commodity.history()->last_lookup);
|
||||||
|
}
|
||||||
DEBUG_("pricing_leeway is " << pricing_leeway);
|
DEBUG_("pricing_leeway is " << pricing_leeway);
|
||||||
|
|
||||||
if ((commodity.history &&
|
if ((commodity.history() &&
|
||||||
(time_now - commodity.history->last_lookup) < pricing_leeway) ||
|
(now - commodity.history()->last_lookup) < pricing_leeway) ||
|
||||||
(time_now - last) < pricing_leeway ||
|
(last && (now - *last) < pricing_leeway) ||
|
||||||
(price && moment > date && (moment - date) <= pricing_leeway))
|
(moment && date && *moment > *date &&
|
||||||
return;
|
(*moment - *date) <= pricing_leeway))
|
||||||
|
return optional<amount_t>();
|
||||||
|
|
||||||
DEBUG_("downloading quote for symbol " << commodity.symbol);
|
DEBUG_("downloading quote for symbol " << commodity.symbol());
|
||||||
|
|
||||||
char buf[256];
|
char buf[256];
|
||||||
buf[0] = '\0';
|
buf[0] = '\0';
|
||||||
|
|
@ -34,7 +39,7 @@ void quotes_by_script::operator()(commodity_base_t& commodity,
|
||||||
bool success = true;
|
bool success = true;
|
||||||
|
|
||||||
if (FILE * fp = popen((string("getquote \"") +
|
if (FILE * fp = popen((string("getquote \"") +
|
||||||
commodity.symbol + "\"").c_str(), "r")) {
|
commodity.base_symbol() + "\"").c_str(), "r")) {
|
||||||
if (feof(fp) || ! fgets(buf, 255, fp))
|
if (feof(fp) || ! fgets(buf, 255, fp))
|
||||||
success = false;
|
success = false;
|
||||||
if (pclose(fp) != 0)
|
if (pclose(fp) != 0)
|
||||||
|
|
@ -49,31 +54,31 @@ void quotes_by_script::operator()(commodity_base_t& commodity,
|
||||||
|
|
||||||
DEBUG_("downloaded quote: " << buf);
|
DEBUG_("downloaded quote: " << buf);
|
||||||
|
|
||||||
|
amount_t price;
|
||||||
price.parse(buf);
|
price.parse(buf);
|
||||||
commodity.add_price(now, price);
|
commodity.add_price(now, price);
|
||||||
|
|
||||||
commodity.history->last_lookup = time_now;
|
commodity.history()->last_lookup = now;
|
||||||
cache_dirty = true;
|
cache_dirty = true;
|
||||||
|
|
||||||
if (price) {
|
assert(! price_db.empty());
|
||||||
assert(! price_db.empty());
|
|
||||||
|
|
||||||
#if defined(__GNUG__) && __GNUG__ < 3
|
#if defined(__GNUG__) && __GNUG__ < 3
|
||||||
ofstream database(price_db, ios::out | ios::app);
|
ofstream database(price_db, ios::out | ios::app);
|
||||||
#else
|
#else
|
||||||
ofstream database(price_db, std::ios_base::out | std::ios_base::app);
|
ofstream database(price_db, std::ios_base::out | std::ios_base::app);
|
||||||
#endif
|
#endif
|
||||||
#if 0
|
#if 0
|
||||||
// jww (2007-04-18): Need to convert to local time and print
|
// jww (2007-04-18): Need to convert to local time and print
|
||||||
// here, print with UTC timezone specifier
|
// here, print with UTC timezone specifier
|
||||||
database << "P " << now.to_string("%Y/%m/%d %H:%M:%S")
|
database << "P " << now.to_string("%Y/%m/%d %H:%M:%S")
|
||||||
<< " " << commodity.symbol << " " << price << endl;
|
<< " " << commodity.symbol << " " << price << endl;
|
||||||
#endif
|
#endif
|
||||||
}
|
return price;
|
||||||
} else {
|
} else {
|
||||||
throw_(download_error,
|
throw_(download_error,
|
||||||
"Failed to download price for '" << commodity.symbol <<
|
"Failed to download price for '" << commodity.symbol() <<
|
||||||
"' (command: \"getquote " << commodity.symbol << "\")");
|
"' (command: \"getquote " << commodity.base_symbol() << "\")");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
12
src/quotes.h
12
src/quotes.h
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
namespace ledger {
|
namespace ledger {
|
||||||
|
|
||||||
class quotes_by_script : public commodity_base_t::updater_t
|
class quotes_by_script
|
||||||
{
|
{
|
||||||
path price_db;
|
path price_db;
|
||||||
time_duration pricing_leeway;
|
time_duration pricing_leeway;
|
||||||
|
|
@ -18,11 +18,11 @@ class quotes_by_script : public commodity_base_t::updater_t
|
||||||
: price_db(_price_db), pricing_leeway(_pricing_leeway),
|
: price_db(_price_db), pricing_leeway(_pricing_leeway),
|
||||||
cache_dirty(_cache_dirty) {}
|
cache_dirty(_cache_dirty) {}
|
||||||
|
|
||||||
virtual void operator()(commodity_base_t& commodity,
|
virtual optional<amount_t>
|
||||||
const ptime& moment,
|
operator()(commodity_t& commodity,
|
||||||
const ptime& date,
|
const optional<moment_t>& date,
|
||||||
const ptime& last,
|
const optional<moment_t>& moment,
|
||||||
amount_t& price);
|
const optional<moment_t>& last);
|
||||||
};
|
};
|
||||||
|
|
||||||
DECLARE_EXCEPTION(download_error);
|
DECLARE_EXCEPTION(download_error);
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,41 @@
|
||||||
|
|
||||||
namespace ledger {
|
namespace ledger {
|
||||||
|
|
||||||
|
session_t * session_t::current = NULL;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
boost::mutex session_t::session_mutex;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void initialize();
|
||||||
|
static void shutdown();
|
||||||
|
|
||||||
|
void set_session_context(session_t * session)
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
session_t::session_mutex.lock();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (session && ! session_t::current) {
|
||||||
|
initialize();
|
||||||
|
}
|
||||||
|
else if (! session && session_t::current) {
|
||||||
|
shutdown();
|
||||||
|
#if 0
|
||||||
|
session_t::session_mutex.unlock();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
session_t::current = session;
|
||||||
|
}
|
||||||
|
|
||||||
|
void release_session_context()
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
session_t::session_mutex.unlock();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
unsigned int session_t::read_journal(std::istream& in,
|
unsigned int session_t::read_journal(std::istream& in,
|
||||||
journal_t * journal,
|
journal_t * journal,
|
||||||
account_t * master,
|
account_t * master,
|
||||||
|
|
@ -203,26 +238,16 @@ xml::xpath_t::op_t * session_t::lookup(const string& name)
|
||||||
|
|
||||||
// jww (2007-04-26): All of Ledger should be accessed through a
|
// jww (2007-04-26): All of Ledger should be accessed through a
|
||||||
// session_t object
|
// session_t object
|
||||||
void initialize()
|
static void initialize()
|
||||||
{
|
{
|
||||||
IF_VERIFY()
|
|
||||||
initialize_memory_tracing();
|
|
||||||
|
|
||||||
amount_t::initialize();
|
amount_t::initialize();
|
||||||
xml::xpath_t::initialize();
|
xml::xpath_t::initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void shutdown()
|
static void shutdown()
|
||||||
{
|
{
|
||||||
xml::xpath_t::shutdown();
|
xml::xpath_t::shutdown();
|
||||||
amount_t::shutdown();
|
amount_t::shutdown();
|
||||||
|
|
||||||
IF_VERIFY() {
|
|
||||||
INFO("Ledger ended (Boost/libstdc++ may still hold memory)");
|
|
||||||
shutdown_memory_tracing();
|
|
||||||
} else {
|
|
||||||
INFO("Ledger ended");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ledger
|
} // namespace ledger
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@ namespace ledger {
|
||||||
class session_t : public xml::xpath_t::scope_t
|
class session_t : public xml::xpath_t::scope_t
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
static session_t * current;
|
||||||
|
|
||||||
path data_file;
|
path data_file;
|
||||||
optional<path> init_file;
|
optional<path> init_file;
|
||||||
optional<path> cache_file;
|
optional<path> cache_file;
|
||||||
|
|
@ -185,8 +187,16 @@ class session_t : public xml::xpath_t::scope_t
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
void initialize();
|
/**
|
||||||
void shutdown();
|
* This sets the current session context, transferring all static
|
||||||
|
* globals to point at the data structures related to this session.
|
||||||
|
* Although Ledger itself is not thread-safe, by locking, switching
|
||||||
|
* session context, then unlocking after the operation is done,
|
||||||
|
* multiple threads can sequentially make use of the library. Thus, a
|
||||||
|
* session_t maintains all of the information relating to a single
|
||||||
|
* usage of the Ledger library.
|
||||||
|
*/
|
||||||
|
void set_session_context(session_t * session = NULL);
|
||||||
|
|
||||||
} // namespace ledger
|
} // namespace ledger
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@ extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
#include <boost/any.hpp>
|
#include <boost/cast.hpp>
|
||||||
#include <boost/current_function.hpp>
|
#include <boost/current_function.hpp>
|
||||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||||
#include <boost/filesystem/convenience.hpp>
|
#include <boost/filesystem/convenience.hpp>
|
||||||
|
|
@ -106,11 +106,16 @@ extern "C" {
|
||||||
#include <boost/filesystem/fstream.hpp>
|
#include <boost/filesystem/fstream.hpp>
|
||||||
#include <boost/filesystem/operations.hpp>
|
#include <boost/filesystem/operations.hpp>
|
||||||
#include <boost/filesystem/path.hpp>
|
#include <boost/filesystem/path.hpp>
|
||||||
#include <boost/lexical_cast.hpp>
|
#include <boost/function.hpp>
|
||||||
|
#include <boost/multi_index_container.hpp>
|
||||||
|
#include <boost/multi_index/hashed_index.hpp>
|
||||||
|
#include <boost/multi_index/key_extractors.hpp>
|
||||||
|
#include <boost/multi_index/ordered_index.hpp>
|
||||||
#include <boost/operators.hpp>
|
#include <boost/operators.hpp>
|
||||||
#include <boost/optional.hpp>
|
#include <boost/optional.hpp>
|
||||||
#include <boost/ptr_container/ptr_list.hpp>
|
#include <boost/ptr_container/ptr_list.hpp>
|
||||||
#include <boost/ptr_container/ptr_vector.hpp>
|
#include <boost/ptr_container/ptr_vector.hpp>
|
||||||
#include <boost/regex.hpp>
|
#include <boost/regex.hpp>
|
||||||
|
#include <boost/signal.hpp>
|
||||||
|
|
||||||
#endif // _SYSTEM_HH
|
#endif // _SYSTEM_HH
|
||||||
|
|
|
||||||
|
|
@ -263,9 +263,9 @@ transaction_t * parse_transaction(char * line,
|
||||||
|
|
||||||
if (xact->amount->commodity() &&
|
if (xact->amount->commodity() &&
|
||||||
! xact->amount->commodity().annotated)
|
! xact->amount->commodity().annotated)
|
||||||
xact->amount->annotate_commodity(per_unit_cost,
|
xact->amount->annotate_commodity(annotation_t(per_unit_cost,
|
||||||
xact->entry->actual_date(),
|
xact->entry->actual_date(),
|
||||||
xact->entry->code);
|
xact->entry->code));
|
||||||
|
|
||||||
DEBUG("ledger.textual.parse", "line " << linenum << ": " <<
|
DEBUG("ledger.textual.parse", "line " << linenum << ": " <<
|
||||||
"Total cost is " << *xact->cost);
|
"Total cost is " << *xact->cost);
|
||||||
|
|
@ -718,7 +718,7 @@ unsigned int textual_parser_t::parse(std::istream& in,
|
||||||
|
|
||||||
case 'D': { // a default commodity for "entry"
|
case 'D': { // a default commodity for "entry"
|
||||||
amount_t amt(skip_ws(line + 1));
|
amount_t amt(skip_ws(line + 1));
|
||||||
commodity_t::default_commodity = &amt.commodity();
|
amount_t::default_pool->default_commodity = &amt.commodity();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -756,7 +756,8 @@ unsigned int textual_parser_t::parse(std::istream& in,
|
||||||
parse_symbol(symbol_and_price, symbol);
|
parse_symbol(symbol_and_price, symbol);
|
||||||
amount_t price(symbol_and_price);
|
amount_t price(symbol_and_price);
|
||||||
|
|
||||||
if (commodity_t * commodity = commodity_t::find_or_create(symbol))
|
if (commodity_t * commodity =
|
||||||
|
amount_t::default_pool->find_or_create(symbol))
|
||||||
commodity->add_price(datetime, price);
|
commodity->add_price(datetime, price);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -766,7 +767,8 @@ unsigned int textual_parser_t::parse(std::istream& in,
|
||||||
string symbol;
|
string symbol;
|
||||||
parse_symbol(p, symbol);
|
parse_symbol(p, symbol);
|
||||||
|
|
||||||
if (commodity_t * commodity = commodity_t::find_or_create(symbol))
|
if (commodity_t * commodity =
|
||||||
|
amount_t::default_pool->find_or_create(symbol))
|
||||||
commodity->add_flags(COMMODITY_STYLE_NOMARKET);
|
commodity->add_flags(COMMODITY_STYLE_NOMARKET);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -774,7 +776,7 @@ unsigned int textual_parser_t::parse(std::istream& in,
|
||||||
case 'Y': // set current year
|
case 'Y': // set current year
|
||||||
#if 0
|
#if 0
|
||||||
// jww (2007-04-18): Need to set this up again
|
// jww (2007-04-18): Need to set this up again
|
||||||
date_t::current_year = lexical_cast<int>(skip_ws(line + 1));
|
date_t::current_year = std::atoi(skip_ws(line + 1));
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,11 @@
|
||||||
#ifndef _UTILS_H
|
#ifndef _UTILS_H
|
||||||
#define _UTILS_H
|
#define _UTILS_H
|
||||||
|
|
||||||
|
#if defined(FULL_DEBUG)
|
||||||
|
#define BOOST_MULTI_INDEX_ENABLE_SAFE_MODE 1
|
||||||
|
#define BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING 1
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <system.hh>
|
#include <system.hh>
|
||||||
|
|
||||||
/**********************************************************************
|
/**********************************************************************
|
||||||
|
|
|
||||||
126
src/value.cc
126
src/value.cc
|
|
@ -1647,7 +1647,7 @@ void value_t::in_place_cast(type_t cast_type)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (alldigits) {
|
if (alldigits) {
|
||||||
long temp = lexical_cast<long>((*(string **) data)->c_str());
|
long temp = std::atol((*(string **) data)->c_str());
|
||||||
destroy();
|
destroy();
|
||||||
*(long *) data = temp;
|
*(long *) data = temp;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1840,7 +1840,7 @@ bool value_t::realzero() const
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
value_t value_t::value(const moment_t& moment) const
|
value_t value_t::value(const optional<moment_t>& moment) const
|
||||||
{
|
{
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case BOOLEAN:
|
case BOOLEAN:
|
||||||
|
|
@ -1849,16 +1849,30 @@ value_t value_t::value(const moment_t& moment) const
|
||||||
throw_(value_error, "Cannot find the value of a date/time");
|
throw_(value_error, "Cannot find the value of a date/time");
|
||||||
case INTEGER:
|
case INTEGER:
|
||||||
return *this;
|
return *this;
|
||||||
case AMOUNT:
|
|
||||||
return ((amount_t *) data)->value(moment);
|
case AMOUNT: {
|
||||||
case BALANCE:
|
if (optional<amount_t> val = ((amount_t *) data)->value(moment))
|
||||||
return ((balance_t *) data)->value(moment);
|
return *val;
|
||||||
case BALANCE_PAIR:
|
return false;
|
||||||
return ((balance_pair_t *) data)->quantity.value(moment);
|
}
|
||||||
|
case BALANCE: {
|
||||||
|
if (optional<balance_t> bal = ((balance_t *) data)->value(moment))
|
||||||
|
return *bal;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
case BALANCE_PAIR: {
|
||||||
|
if (optional<balance_t> bal =
|
||||||
|
((balance_pair_t *) data)->quantity.value(moment))
|
||||||
|
return *bal;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
case STRING:
|
case STRING:
|
||||||
throw_(value_error, "Cannot find the value of a string");
|
throw_(value_error, "Cannot find the value of a string");
|
||||||
|
|
||||||
case XML_NODE:
|
case XML_NODE:
|
||||||
return (*(xml::node_t **) data)->to_value().value(moment);
|
return (*(xml::node_t **) data)->to_value().value(moment);
|
||||||
|
|
||||||
case POINTER:
|
case POINTER:
|
||||||
throw_(value_error, "Cannot find the value of a pointer");
|
throw_(value_error, "Cannot find the value of a pointer");
|
||||||
case SEQUENCE:
|
case SEQUENCE:
|
||||||
|
|
@ -1954,45 +1968,37 @@ value_t value_t::unround() const
|
||||||
return value_t();
|
return value_t();
|
||||||
}
|
}
|
||||||
|
|
||||||
value_t value_t::price() const
|
value_t value_t::annotated_price() const
|
||||||
{
|
{
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case BOOLEAN:
|
case BOOLEAN:
|
||||||
throw_(value_error, "Cannot find the price of a boolean");
|
throw_(value_error, "Cannot find the annotated price of a boolean");
|
||||||
case INTEGER:
|
case INTEGER:
|
||||||
return *this;
|
return *this;
|
||||||
case DATETIME:
|
case DATETIME:
|
||||||
throw_(value_error, "Cannot find the price of a date/time");
|
throw_(value_error, "Cannot find the annotated price of a date/time");
|
||||||
|
|
||||||
case AMOUNT: {
|
case AMOUNT: {
|
||||||
optional<amount_t> temp = ((amount_t *) data)->price();
|
optional<amount_t> temp = ((amount_t *) data)->annotation_details().price;
|
||||||
if (! temp)
|
|
||||||
return false;
|
|
||||||
return *temp;
|
|
||||||
}
|
|
||||||
case BALANCE: {
|
|
||||||
optional<balance_t> temp = ((balance_t *) data)->price();
|
|
||||||
if (! temp)
|
|
||||||
return false;
|
|
||||||
return *temp;
|
|
||||||
}
|
|
||||||
case BALANCE_PAIR: {
|
|
||||||
optional<balance_t> temp = ((balance_pair_t *) data)->price();
|
|
||||||
if (! temp)
|
if (! temp)
|
||||||
return false;
|
return false;
|
||||||
return *temp;
|
return *temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case BALANCE:
|
||||||
|
throw_(value_error, "Cannot find the annotated price of a balance");
|
||||||
|
case BALANCE_PAIR:
|
||||||
|
throw_(value_error, "Cannot find the annotated price of a balance pair");
|
||||||
case STRING:
|
case STRING:
|
||||||
throw_(value_error, "Cannot find the price of a string");
|
throw_(value_error, "Cannot find the annotated price of a string");
|
||||||
|
|
||||||
case XML_NODE:
|
case XML_NODE:
|
||||||
return (*(xml::node_t **) data)->to_value().price();
|
return (*(xml::node_t **) data)->to_value().annotated_price();
|
||||||
|
|
||||||
case POINTER:
|
case POINTER:
|
||||||
throw_(value_error, "Cannot find the price of a pointer");
|
throw_(value_error, "Cannot find the annotated price of a pointer");
|
||||||
case SEQUENCE:
|
case SEQUENCE:
|
||||||
throw_(value_error, "Cannot find the price of a sequence");
|
throw_(value_error, "Cannot find the annotated price of a sequence");
|
||||||
|
|
||||||
default:
|
default:
|
||||||
assert(0);
|
assert(0);
|
||||||
|
|
@ -2002,46 +2008,38 @@ value_t value_t::price() const
|
||||||
return value_t();
|
return value_t();
|
||||||
}
|
}
|
||||||
|
|
||||||
value_t value_t::date() const
|
value_t value_t::annotated_date() const
|
||||||
{
|
{
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case BOOLEAN:
|
case BOOLEAN:
|
||||||
throw_(value_error, "Cannot find the date of a boolean");
|
throw_(value_error, "Cannot find the annotated date of a boolean");
|
||||||
case INTEGER:
|
case INTEGER:
|
||||||
throw_(value_error, "Cannot find the date of an integer");
|
throw_(value_error, "Cannot find the annotated date of an integer");
|
||||||
|
|
||||||
case DATETIME:
|
case DATETIME:
|
||||||
return *this;
|
return *this;
|
||||||
|
|
||||||
case AMOUNT: {
|
case AMOUNT: {
|
||||||
optional<moment_t> temp = ((amount_t *) data)->date();
|
optional<moment_t> temp = ((amount_t *) data)->annotation_details().date;
|
||||||
if (! temp)
|
|
||||||
return false;
|
|
||||||
return *temp;
|
|
||||||
}
|
|
||||||
case BALANCE: {
|
|
||||||
optional<moment_t> temp = ((balance_t *) data)->date();
|
|
||||||
if (! temp)
|
|
||||||
return false;
|
|
||||||
return *temp;
|
|
||||||
}
|
|
||||||
case BALANCE_PAIR: {
|
|
||||||
optional<moment_t> temp = ((balance_pair_t *) data)->date();
|
|
||||||
if (! temp)
|
if (! temp)
|
||||||
return false;
|
return false;
|
||||||
return *temp;
|
return *temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case BALANCE:
|
||||||
|
throw_(value_error, "Cannot find the annotated date of a balance");
|
||||||
|
case BALANCE_PAIR:
|
||||||
|
throw_(value_error, "Cannot find the annotated date of a balance pair");
|
||||||
case STRING:
|
case STRING:
|
||||||
throw_(value_error, "Cannot find the date of a string");
|
throw_(value_error, "Cannot find the annotated date of a string");
|
||||||
|
|
||||||
case XML_NODE:
|
case XML_NODE:
|
||||||
return (*(xml::node_t **) data)->to_value().date();
|
return (*(xml::node_t **) data)->to_value().annotated_date();
|
||||||
|
|
||||||
case POINTER:
|
case POINTER:
|
||||||
throw_(value_error, "Cannot find the date of a pointer");
|
throw_(value_error, "Cannot find the annotated date of a pointer");
|
||||||
case SEQUENCE:
|
case SEQUENCE:
|
||||||
throw_(value_error, "Cannot find the date of a sequence");
|
throw_(value_error, "Cannot find the annotated date of a sequence");
|
||||||
|
|
||||||
default:
|
default:
|
||||||
assert(0);
|
assert(0);
|
||||||
|
|
@ -2051,46 +2049,38 @@ value_t value_t::date() const
|
||||||
return value_t();
|
return value_t();
|
||||||
}
|
}
|
||||||
|
|
||||||
value_t value_t::tag() const
|
value_t value_t::annotated_tag() const
|
||||||
{
|
{
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case BOOLEAN:
|
case BOOLEAN:
|
||||||
throw_(value_error, "Cannot find the date of a boolean");
|
throw_(value_error, "Cannot find the annotated tag of a boolean");
|
||||||
case INTEGER:
|
case INTEGER:
|
||||||
throw_(value_error, "Cannot find the date of an integer");
|
throw_(value_error, "Cannot find the annotated tag of an integer");
|
||||||
|
|
||||||
case DATETIME:
|
case DATETIME:
|
||||||
return *this;
|
return *this;
|
||||||
|
|
||||||
case AMOUNT: {
|
case AMOUNT: {
|
||||||
optional<string> temp = ((amount_t *) data)->tag();
|
optional<string> temp = ((amount_t *) data)->annotation_details().tag;
|
||||||
if (! temp)
|
|
||||||
return false;
|
|
||||||
return *temp;
|
|
||||||
}
|
|
||||||
case BALANCE: {
|
|
||||||
optional<string> temp = ((balance_t *) data)->tag();
|
|
||||||
if (! temp)
|
|
||||||
return false;
|
|
||||||
return *temp;
|
|
||||||
}
|
|
||||||
case BALANCE_PAIR: {
|
|
||||||
optional<string> temp = ((balance_pair_t *) data)->tag();
|
|
||||||
if (! temp)
|
if (! temp)
|
||||||
return false;
|
return false;
|
||||||
return *temp;
|
return *temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case BALANCE:
|
||||||
|
throw_(value_error, "Cannot find the annotated tag of a balance");
|
||||||
|
case BALANCE_PAIR:
|
||||||
|
throw_(value_error, "Cannot find the annotated tag of a balance pair");
|
||||||
case STRING:
|
case STRING:
|
||||||
throw_(value_error, "Cannot find the date of a string");
|
throw_(value_error, "Cannot find the annotated tag of a string");
|
||||||
|
|
||||||
case XML_NODE:
|
case XML_NODE:
|
||||||
return (*(xml::node_t **) data)->to_value().date();
|
return (*(xml::node_t **) data)->to_value().annotated_tag();
|
||||||
|
|
||||||
case POINTER:
|
case POINTER:
|
||||||
throw_(value_error, "Cannot find the date of a pointer");
|
throw_(value_error, "Cannot find the annotated tag of a pointer");
|
||||||
case SEQUENCE:
|
case SEQUENCE:
|
||||||
throw_(value_error, "Cannot find the date of a sequence");
|
throw_(value_error, "Cannot find the annotated tag of a sequence");
|
||||||
|
|
||||||
default:
|
default:
|
||||||
assert(0);
|
assert(0);
|
||||||
|
|
|
||||||
|
|
@ -363,9 +363,9 @@ class value_t
|
||||||
value_t abs() const;
|
value_t abs() const;
|
||||||
void in_place_cast(type_t cast_type);
|
void in_place_cast(type_t cast_type);
|
||||||
value_t cost() const;
|
value_t cost() const;
|
||||||
value_t price() const;
|
value_t annotated_price() const;
|
||||||
value_t date() const;
|
value_t annotated_date() const;
|
||||||
value_t tag() const;
|
value_t annotated_tag() const;
|
||||||
|
|
||||||
value_t cast(type_t cast_type) const {
|
value_t cast(type_t cast_type) const {
|
||||||
value_t temp(*this);
|
value_t temp(*this);
|
||||||
|
|
@ -379,7 +379,8 @@ class value_t
|
||||||
|
|
||||||
value_t& add(const amount_t& amount,
|
value_t& add(const amount_t& amount,
|
||||||
const optional<amount_t>& cost = optional<amount_t>());
|
const optional<amount_t>& cost = optional<amount_t>());
|
||||||
value_t value(const moment_t& moment) const;
|
value_t value(const optional<moment_t>& moment =
|
||||||
|
optional<moment_t>()) const;
|
||||||
|
|
||||||
void in_place_reduce();
|
void in_place_reduce();
|
||||||
value_t reduce() const {
|
value_t reduce() const {
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,7 @@ static void endElement(void *userData, const char *name)
|
||||||
}
|
}
|
||||||
else if (std::strcmp(name, "symbol") == 0) {
|
else if (std::strcmp(name, "symbol") == 0) {
|
||||||
assert(! curr_comm);
|
assert(! curr_comm);
|
||||||
curr_comm = commodity_t::find_or_create(data);
|
curr_comm = amount_t::default_pool->find_or_create(data);
|
||||||
assert(curr_comm);
|
assert(curr_comm);
|
||||||
curr_comm->add_flags(COMMODITY_STYLE_SUFFIXED);
|
curr_comm->add_flags(COMMODITY_STYLE_SUFFIXED);
|
||||||
if (! comm_flags.empty()) {
|
if (! comm_flags.empty()) {
|
||||||
|
|
|
||||||
|
|
@ -654,7 +654,7 @@ xpath_t::parse_value_term(std::istream& in, unsigned short tflags) const
|
||||||
int id = -1;
|
int id = -1;
|
||||||
if (std::isdigit(ident[0])) {
|
if (std::isdigit(ident[0])) {
|
||||||
node.reset(new op_t(op_t::ARG_INDEX));
|
node.reset(new op_t(op_t::ARG_INDEX));
|
||||||
node->arg_index = lexical_cast<unsigned int>(ident.c_str());
|
node->arg_index = std::atol(ident.c_str());
|
||||||
}
|
}
|
||||||
else if ((id = document_t::lookup_builtin_id(ident)) != -1) {
|
else if ((id = document_t::lookup_builtin_id(ident)) != -1) {
|
||||||
node.reset(new op_t(op_t::NODE_ID));
|
node.reset(new op_t(op_t::NODE_ID));
|
||||||
|
|
@ -2286,7 +2286,7 @@ bool xpath_t::op_t::write(std::ostream& out,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! symbol.empty()) {
|
if (! symbol.empty()) {
|
||||||
if (commodity_t::find(symbol))
|
if (amount_t::default_pool->find(symbol))
|
||||||
out << '@';
|
out << '@';
|
||||||
out << symbol;
|
out << symbol;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,10 @@
|
||||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(BasicAmountTestCase, "numerics");
|
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(BasicAmountTestCase, "numerics");
|
||||||
|
|
||||||
void BasicAmountTestCase::setUp() {
|
void BasicAmountTestCase::setUp() {
|
||||||
ledger::initialize();
|
ledger::set_session_context(&session);
|
||||||
}
|
}
|
||||||
void BasicAmountTestCase::tearDown() {
|
void BasicAmountTestCase::tearDown() {
|
||||||
ledger::shutdown();
|
ledger::set_session_context();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BasicAmountTestCase::testConstructors()
|
void BasicAmountTestCase::testConstructors()
|
||||||
|
|
@ -17,8 +17,8 @@ void BasicAmountTestCase::testConstructors()
|
||||||
amount_t x3(123.456);
|
amount_t x3(123.456);
|
||||||
amount_t x5("123456");
|
amount_t x5("123456");
|
||||||
amount_t x6("123.456");
|
amount_t x6("123.456");
|
||||||
amount_t x7(string("123456"));
|
amount_t x7(std::string("123456"));
|
||||||
amount_t x8(string("123.456"));
|
amount_t x8(std::string("123.456"));
|
||||||
amount_t x9(x3);
|
amount_t x9(x3);
|
||||||
amount_t x10(x6);
|
amount_t x10(x6);
|
||||||
amount_t x11(x8);
|
amount_t x11(x8);
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,8 @@ class BasicAmountTestCase : public CPPUNIT_NS::TestCase
|
||||||
CPPUNIT_TEST_SUITE_END();
|
CPPUNIT_TEST_SUITE_END();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
ledger::session_t session;
|
||||||
|
|
||||||
BasicAmountTestCase() {}
|
BasicAmountTestCase() {}
|
||||||
virtual ~BasicAmountTestCase() {}
|
virtual ~BasicAmountTestCase() {}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,10 @@
|
||||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(CommodityTestCase, "numerics");
|
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(CommodityTestCase, "numerics");
|
||||||
|
|
||||||
void CommodityTestCase::setUp() {
|
void CommodityTestCase::setUp() {
|
||||||
ledger::initialize();
|
ledger::set_session_context(&session);
|
||||||
}
|
}
|
||||||
void CommodityTestCase::tearDown() {
|
void CommodityTestCase::tearDown() {
|
||||||
ledger::shutdown();
|
ledger::set_session_context();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommodityTestCase::testPriceHistory()
|
void CommodityTestCase::testPriceHistory()
|
||||||
|
|
@ -32,8 +32,13 @@ void CommodityTestCase::testPriceHistory()
|
||||||
aapl.add_price(mar01_07, amount_t("$19.50"));
|
aapl.add_price(mar01_07, amount_t("$19.50"));
|
||||||
aapl.add_price(apr15_07, amount_t("$21.22"));
|
aapl.add_price(apr15_07, amount_t("$21.22"));
|
||||||
|
|
||||||
assertEqual(amount_t("$1831.83"), x1.value(feb28_07sbm));
|
optional<amount_t> amt1 = x1.value(feb28_07sbm);
|
||||||
assertEqual(amount_t("$2124.12"), x1.value(now));
|
assertTrue(amt1);
|
||||||
|
assertEqual(amount_t("$1831.83"), *amt1);
|
||||||
|
|
||||||
|
optional<amount_t> amt2 = x1.value(now);
|
||||||
|
assertTrue(amt2);
|
||||||
|
assertEqual(amount_t("$2124.12"), *amt2);
|
||||||
|
|
||||||
assertValid(x1);
|
assertValid(x1);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@ class CommodityTestCase : public CPPUNIT_NS::TestCase
|
||||||
CPPUNIT_TEST_SUITE_END();
|
CPPUNIT_TEST_SUITE_END();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
ledger::session_t session;
|
||||||
|
|
||||||
CommodityTestCase() {}
|
CommodityTestCase() {}
|
||||||
virtual ~CommodityTestCase() {}
|
virtual ~CommodityTestCase() {}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(CommodityAmountTestCase, "numerics");
|
||||||
|
|
||||||
void CommodityAmountTestCase::setUp()
|
void CommodityAmountTestCase::setUp()
|
||||||
{
|
{
|
||||||
ledger::initialize();
|
ledger::set_session_context(&session);
|
||||||
|
|
||||||
// Cause the display precision for dollars to be initialized to 2.
|
// Cause the display precision for dollars to be initialized to 2.
|
||||||
amount_t x1("$1.00");
|
amount_t x1("$1.00");
|
||||||
|
|
@ -17,7 +17,8 @@ void CommodityAmountTestCase::setUp()
|
||||||
void CommodityAmountTestCase::tearDown()
|
void CommodityAmountTestCase::tearDown()
|
||||||
{
|
{
|
||||||
amount_t::full_strings = false;
|
amount_t::full_strings = false;
|
||||||
ledger::shutdown();
|
|
||||||
|
ledger::set_session_context();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommodityAmountTestCase::testConstructors()
|
void CommodityAmountTestCase::testConstructors()
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,8 @@ class CommodityAmountTestCase : public CPPUNIT_NS::TestCase
|
||||||
CPPUNIT_TEST_SUITE_END();
|
CPPUNIT_TEST_SUITE_END();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
ledger::session_t session;
|
||||||
|
|
||||||
CommodityAmountTestCase() {}
|
CommodityAmountTestCase() {}
|
||||||
virtual ~CommodityAmountTestCase() {}
|
virtual ~CommodityAmountTestCase() {}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue