Merge branch 'next'

This commit is contained in:
John Wiegley 2009-11-20 21:33:23 -05:00
commit 117dddabd4
33 changed files with 922 additions and 301 deletions

View file

@ -45,17 +45,24 @@ much further with those.
h2. Dependencies h2. Dependencies
If you wish to proceed in this venture, you'll need a few dependencies: If you wish to proceed in this venture, you'll need a few dependencies. The
easiest way to get them for your platform is to run:
<pre>
./acprep dependencies
</pre>
If that doesn't completely work, read on.
h3. For building the current master branch h3. For building the current master branch
|_.Library|_.Min.Ver.|_.When needed| |_.Library|_.Min.Ver.|_.When needed|
|Boost|1.35 or higher|| |Boost|1.35||
|GMP|4.2.2|| |GMP|4.2.2||
|MPFR|2.4.0|| |MPFR|2.4.0||
|gettext|0.17|_optional_| |gettext|0.17|_optional_|
|libedit|20090111-3.0|_optional_| |libedit|20090111-3.0|_optional_|
|Python|2.4 or higher|_optional_| |Python|2.4|_optional_|
|cppunit|1.12.1|_optional_, for @make check@| |cppunit|1.12.1|_optional_, for @make check@|
|doxygen|1.5.7.1|_optional_, for @make docs@| |doxygen|1.5.7.1|_optional_, for @make docs@|
|graphviz|2.20.3|_optional_, for @make docs@| |graphviz|2.20.3|_optional_, for @make docs@|
@ -63,7 +70,7 @@ h3. For building the current master branch
|lcov|1.6|_optional_, for @make report@, used with @./acprep gcov@| |lcov|1.6|_optional_, for @make report@, used with @./acprep gcov@|
|sloccount|2.26|_optional_, for @make sloc@| |sloccount|2.26|_optional_, for @make sloc@|
h3. For building the beta or release branches h3. For building the current @maint@ branch
|_.Library|_.Min.Ver.|_.When needed| |_.Library|_.Min.Ver.|_.When needed|
|GMP|4.2.2|| |GMP|4.2.2||
@ -74,12 +81,16 @@ h3. For building the beta or release branches
h3. MacPorts h3. MacPorts
If you build stuff using MacPorts, as I do, here is what you would run: If you build stuff using MacPorts on OS X, as I do, here is what you would
run:
<pre> <pre>
sudo port install boost +python25+st gmp mpfr gettext libedit \ sudo port install -f automake autoconf libtool python26
cppunit texlive doxygen graphviz texinfo lcov \ libiconv +universal zlib +universal gmp +universal
sloccount pcre libofx expat mpfr +universal ncurses +universal ncursesw +universal
gettext +universal libedit +universal boost-jam
boost +st+python26+icu cppunit texlive doxygen graphviz
texinfo lcov sloccount
</pre> </pre>
You can even just install the current Ledger *RELEASE* directly: You can even just install the current Ledger *RELEASE* directly:
@ -90,25 +101,38 @@ sudo port install ledger
h3. Ubuntu h3. Ubuntu
If you're going to be build on Ubuntu, @sudo apt-get install ...@ If you're going to be build on Ubuntu, @sudo apt-get install ...@ the
the following packages (current as of Ubuntu Hardy): following packages (current as of Ubuntu Hardy):
<pre>
sudo apt-get install build-essential libtool autoconf automake \
zlib1g-dev libbz2-dev python-dev bjam cvs gettext libgmp3-dev \
libmpfr-dev libboost1.35-dev libboost-regex1.35-dev \
libboost-date-time1.35-dev libboost-filesystem1.35-dev \
libboost-python1.35-dev texinfo lcov sloccount
</pre>
Or, for Ubuntu Karmic:
<pre> <pre>
sudo apt-get install build-essential libtool autoconf automake \ sudo apt-get install build-essential libtool autoconf automake \
texinfo python-dev zlib1g-dev libbz2-dev stow libgmp3-dev \ texinfo python-dev zlib1g-dev libbz2-dev libgmp3-dev \
bjam libboost1.35-dev libboost-regex1.35-dev \ bjam gettext cvs libboost1.40-dev libboost-regex1.40-dev \
libboost-date-time1.35-dev libboost-filesystem1.35-dev libboost-date-time1.40-dev libboost-filesystem1.40-dev \
libmpfr-dev
</pre> </pre>
h2. Preparing the Build h2. Building
The next step is preparing your environment for building. While you can use The next step is preparing your environment for building. While you can use
@autogen.sh@, I've prepared a script that does a lot more of the footwork for @autogen.sh@, ./configure and make, I've prepared a script that does a lot more
you: of the footwork for you:
<pre> <pre>
./acprep pull # Make sure everything is pulled that needs to be ./acprep update
./acprep # or, if you want to use the Boost libraries with suffix -mt, install in
# $HOME/local and build with 2 processes in parallel
./acprep update --boost=-mt -- --prefix=$HOME/local -j2
</pre> </pre>
Please read the contents of @config.log@ if the configure step fails. Also, Please read the contents of @config.log@ if the configure step fails. Also,
@ -116,10 +140,7 @@ see the @help@ command to @acprep@, which explains some of its many options.
It's pretty much the only command I run for configuring, building and testing It's pretty much the only command I run for configuring, building and testing
Ledger. Ledger.
h2. Building You can run @make check@ to confirm the result, and @make install@ to install.
Once you have the dependencies installed and the source prepared for building,
run @make check@ to get things started and confirm the result.
If you have extra CPU cycles to burn, try @./acprep proof@, which provides the If you have extra CPU cycles to burn, try @./acprep proof@, which provides the
most thorough shakedown of a healthy source tree. most thorough shakedown of a healthy source tree.

111
acprep
View file

@ -183,7 +183,7 @@ class PrepareBuild(CommandLineApp):
self.current_flavor = 'debug' self.current_flavor = 'debug'
self.products_dir = None self.products_dir = None
self.build_dir = self.source_dir self.build_dir = self.source_dir
self.configure_args = ['--with-included-gettext'] self.configure_args = ['--with-included-gettext', '--enable-python']
self.sys_include_dirs = [] self.sys_include_dirs = []
self.sys_library_dirs = [] self.sys_library_dirs = []
@ -551,14 +551,27 @@ class PrepareBuild(CommandLineApp):
self.log.info('Looks like you are using MacPorts on OS X') self.log.info('Looks like you are using MacPorts on OS X')
packages = [ packages = [
'sudo', 'port', 'install', '-f', 'sudo', 'port', 'install', '-f',
'automake', 'autoconf', 'libtool', 'python26', 'automake',
'libiconv', '+universal', 'zlib', '+universal', 'autoconf',
'gmp' ,'+universal', 'mpfr', '+universal', 'libtool',
'ncurses', '+universal', 'ncursesw', '+universal', 'python26',
'gettext' ,'+universal', 'libedit' ,'+universal', 'libiconv', '+universal',
'boost-jam', 'boost', '+st+python26+icu', 'cppunit', 'zlib', '+universal',
'texlive', 'doxygen', 'graphviz', 'texinfo', 'gmp' ,'+universal',
'lcov', 'sloccount' 'mpfr', '+universal',
'ncurses', '+universal',
'ncursesw', '+universal',
'gettext' ,'+universal',
'libedit' ,'+universal',
'boost-jam',
'boost', '+st+python26+icu',
'cppunit',
#'texlive',
#'doxygen',
#'graphviz',
'texinfo',
'lcov',
'sloccount'
] ]
self.log.info('Executing: ' + string.join(packages, ' ')) self.log.info('Executing: ' + string.join(packages, ' '))
self.execute(*packages) self.execute(*packages)
@ -570,23 +583,69 @@ class PrepareBuild(CommandLineApp):
if exists('/etc/issue'): if exists('/etc/issue'):
issue = open('/etc/issue') issue = open('/etc/issue')
if issue.readline().startswith('Ubuntu'): if issue.readline().startswith('Ubuntu'):
self.log.info('Looks like you are using APT on Ubuntu') release = open('/etc/lsb-release')
packages = [ info = release.read()
'sudo', 'apt-get', 'install', release.close()
'build-essential', if re.search('karmic', info):
'libtool', 'autoconf', 'automake', self.log.info('Looks like you are using APT on Ubuntu Karmic')
'zlib1g-dev', 'libbz2-dev', 'python-dev', packages = [
'libboost1.35-dev', 'sudo', 'apt-get', 'install',
'libboost-python1.35-dev', 'build-essential',
'libboost-regex1.35-dev', 'libtool',
'libboost-date-time1.35-dev', 'autoconf',
'libboost-filesystem1.35-dev' 'automake',
'libgmp3-dev', 'libmpfr-dev', 'gettext', 'zlib1g-dev',
'libedit-dev', 'libcppunit-dev', 'libbz2-dev',
#'texlive-full', 'python-dev',
#'doxygen', 'graphviz', 'texinfo', 'libgmp3-dev',
'lcov', 'sloccount' 'libmpfr-dev',
] 'bjam',
'gettext',
'cvs',
'libboost1.40-dev',
'libboost-regex1.40-dev',
'libboost-date-time1.40-dev',
'libboost-filesystem1.40-dev'
'libboost-python1.40-dev',
'libedit-dev',
'libcppunit-dev',
#'texlive-full',
#'doxygen',
#'graphviz',
'texinfo',
'lcov',
'sloccount'
]
else:
self.log.info('Looks like you are using APT on Ubuntu Hardy')
packages = [
'sudo', 'apt-get', 'install',
'build-essential',
'libtool',
'autoconf',
'automake',
'zlib1g-dev',
'libbz2-dev',
'python-dev',
'bjam',
'cvs',
'gettext',
'libgmp3-dev',
'libmpfr-dev',
'libboost1.35-dev',
'libboost-python1.35-dev',
'libboost-regex1.35-dev',
'libboost-date-time1.35-dev',
'libboost-filesystem1.35-dev'
'libedit-dev',
'libcppunit-dev',
#'texlive-full',
#'doxygen',
#'graphviz',
'texinfo',
'lcov',
'sloccount'
]
self.log.info('Executing: ' + string.join(packages, ' ')) self.log.info('Executing: ' + string.join(packages, ' '))
self.execute(*packages) self.execute(*packages)

View file

@ -1,38 +1,44 @@
; -*- ledger -*-
N $ N $
= account =~ /^Expenses:Books/ = /^Expenses:Books/
(Liabilities:Taxes) -0.10 (Liabilities:Taxes) -0.10
~ Monthly ~ Monthly
Assets:Bank:Checking $500.00 Assets:Bank:Checking $500.00
Income:Salary Income:Salary
~ Yearly
Expenses:Donations $100.00
Assets:Bank:Checking
2004/05/01 * Checking balance 2004/05/01 * Checking balance
Assets:Bank:Checking $1,000.00 Assets:Bank:Checking $1,000.00
Equity:Opening Balances Equity:Opening Balances
2004/05/03=2004/05/01 * Investment balance 2004/05/03=2004/05/01 * Investment balance
Assets:Brokerage 50 AAPL @ $30.00 Assets:Brokerage 50 AAPL @ $30.00
Equity:Opening Balances Equity:Opening Balances
2004/05/14 * Páy dày 2004/05/14 * Páy dày
Assets:Bank:Checking 500.00€ Assets:Bank:Checking 500.00€
Income:Salary Income:Salary
2004/05/14 * Another dày in which there is Páying 2004/05/14 * Another dày in which there is Páying
Asséts:Bánk:Chécking:Asséts:Bánk:Chécking $500.00 Asséts:Bánk:Chécking:Asséts:Bánk:Chécking $500.00
Income:Salary Income:Salary
2004/05/14 * Another dày in which there is Páying 2004/05/14 * Another dày in which there is Páying
Русский язык:Активы:Русский язык:Русский язык $1000.00 Русский язык:Активы:Русский язык:Русский язык $1000.00
Income:Salary Income:Salary
tag foo tag foo
2004/05/27 Book Store 2004/05/27 Book Store
Expenses:Books $20.00 Expenses:Books $20.00
Expenses:Cards $40.00 Expenses:Cards $40.00
Expenses:Docs $30.00 Expenses:Docs $30.00
Liabilities:MasterCard Liabilities:MasterCard
end tag end tag
@ -40,7 +46,7 @@ end tag
2004/05/27 (100) Credit card company 2004/05/27 (100) Credit card company
; This is an xact note! ; This is an xact note!
; Sample: Value ; Sample: Value
Liabilities:MasterCard $20.00 Liabilities:MasterCard $20.00
; This is a posting note! ; This is a posting note!
; Sample: Another Value ; Sample: Another Value
; :MyTag: ; :MyTag:

293
python/demo.py Executable file
View file

@ -0,0 +1,293 @@
#!/usr/bin/env python
import sys
from datetime import datetime
# The following literate program will demonstrate, by example, how to use the
# Ledger Python module to access your data and build custom reports using the
# magic of Python.
import ledger
print "Welcome to the Ledger.Python demo!"
# Some quick helper functions to help us assert various types of truth
# throughout the script.
def assertEqual(pat, candidate):
if pat != candidate:
raise Exception("FAILED: %s != %s" % (pat, candidate))
sys.exit(1)
###############################################################################
#
# COMMODITIES
#
# Every amount in Ledger has a commodity, even if it is the "null commodity".
# What's special about commodities are not just their symbol, but how they
# alter the way amounts are displayed.
#
# For example, internally Ledger uses infinite precision rational numbers,
# which have no decimal point. So how does it know that $1.00 / $0.75 should
# be displayed as $1.33, and not with an infinitely repeating decimal? It
# does it by consulting the commodity.
#
# Whenever an amount is encountered in your data file, Ledger observes how you
# specified it:
# - How many digits of precision did you use?
# - Was the commodity name before or after the amount?
# - Was the commodity separated from the amount by a space?
# - Did you use thousands markers (1,000)?
# - Did you use European-style numbers (1.000,00)?
#
# By tracking this information for each commodity, Ledger knows how you want
# to see the amount in your reports. This way, dollars can be output as
# $123.56, while stock options could be output as 10.113 AAPL.
#
# Your program can access the known set of commodities using the global
# `ledger.commodities'. This object behaves like a dict, and support all of
# the non-modifying dict protocol methods. If you wish to create a new
# commodity without parsing an amount, you can use the method
# `find_or_create':
comms = ledger.commodities
usd = comms.find_or_create('$')
eur = comms.find_or_create('EUR')
xcd = comms.find_or_create('XCD')
assert not comms.find('CAD')
assert not comms.has_key('CAD')
assert not 'CAD' in comms
# The above mentioned commodity display attributes can be set using commodity
# display flags. This is not something you will usually be doing, however, as
# these flags can be inferred correctly from a large enough set of sample
# amounts, such as those found in your data file. If you live in Europe and
# want all amounts to default to the European-style, set the static variable
# `european_by_default'.
eur.add_flags(ledger.COMMODITY_STYLE_EUROPEAN)
assert eur.has_flags(ledger.COMMODITY_STYLE_EUROPEAN)
assert not eur.has_flags(ledger.COMMODITY_STYLE_THOUSANDS)
comms.european_by_default = True
# There are a few built-in commodities: null, %, h, m and s. Normally you
# don't need to worry about them, but they'll show up if you examine all the
# keys in the commodities dict.
assertEqual([u'', u'$', u'%', u'EUR', u'XCD', u'h', u'm', u's'],
sorted(comms.keys()))
# All the styles of dict iteration are supported:
for symbol in comms.iterkeys():
pass
for commodity in comms.itervalues():
pass
#for symbol, commodity in comms.iteritems():
# pass
#for symbol, commodity in comms:
# pass
# Another important thing about commodities is that they remember if they've
# been exchanged for another commodity, and what the conversion rate was on
# that date. You can record specific conversion rates for any date using the
# `exchange' method.
comms.exchange(eur, ledger.Amount('$0.77')) # Trade 1 EUR for $0.77
comms.exchange(eur, ledger.Amount('$0.66'), datetime.now())
# For the most part, however, you won't be interacting with commodities
# directly, except maybe to look at their `symbol'.
assertEqual('$', usd.symbol)
assertEqual('$', comms['$'].symbol)
###############################################################################
#
# AMOUNTS & BALANCES
#
# Ledger deals with two basic numerical values: Amount and Balance objects.
# An Amount is an infinite-precision rational with an associated commodity
# (even if it is the null commodity, which is called an "uncommoditized
# amount"). A Balance is a collection of Amounts of differing commodities.
#
# Amounts support all the math operations you might expect of an integer,
# except it carries a commodity. Let's take dollars for example:
zero = ledger.Amount("$0")
one = ledger.Amount("$1")
oneb = ledger.Amount("$1")
two = ledger.Amount("$2")
three = ledger.Amount("3") # uncommoditized
assert one == oneb # numeric equality, not identity
assert one != two
assert not zero # tests if it would *display* as a zero
assert one < two
assert one > zero
# For addition and subtraction, only amounts of the same commodity may be
# used, unless one of the amounts has no commodity at all -- in which case the
# result uses the commodity of the other value. Adding $10 to 10 EUR, for
# example, causes an ArithmeticError exception, but adding 10 to $10 gives
# $20.
four = ledger.Amount(two) # make a copy
four += two
assertEqual(four, two + two)
assertEqual(zero, one - one)
try:
two += ledger.Amount("20 EUR")
assert False
except ArithmeticError:
pass
# Use `number' to get the uncommoditized version of an Amount
assertEqual(three, (two + one).number())
# Multiplication and division does supports Amounts of different commodities,
# however:
# - If either amount is uncommoditized, the result carries the commodity of
# the other amount.
# - Otherwise, the result always carries the commodity of the first amount.
five = ledger.Amount("5 CAD")
assertEqual(one, two / two)
assertEqual(five, (five * ledger.Amount("$2")) - ledger.Amount("5"))
# An amount's commodity determines the decimal precision it's displayed with.
# However, this "precision" is a notional thing only. You can tell an amount
# to ignore its display precision by setting `keep_precision' to True.
# (Uncommoditized amounts ignore the value of `keep_precision', and assume it
# is always True). In this case, Ledger does its best to maintain maximal
# precision by watching how the Amount is used. That is, 1.01 * 1.01 yields a
# precision of 4. This tracking is just a best estimate, however, since
# internally Ledger never uses floating-point values.
amt = ledger.Amount('$100.12')
mini = ledger.Amount('0.00045')
assert not amt.keep_precision
assertEqual(5, mini.precision)
assertEqual(5, mini.display_precision) # display_precision == precision
assertEqual(2, amt.precision)
assertEqual(2, amt.display_precision)
mini *= mini
amt *= amt
assertEqual(10, mini.precision)
assertEqual(10, mini.display_precision)
assertEqual(4, amt.precision)
assertEqual(2, amt.display_precision)
# There are several other supported math operations:
amt = ledger.Amount('$100.12')
market = ((ledger.Amount('1 EUR') / ledger.Amount('$0.77')) * amt)
assertEqual(market, amt.value(eur)) # find present market value
assertEqual('$-100.12', str(amt.negated())) # negate the amount
assertEqual('$-100.12', str(- amt)) # negate it more simply
assertEqual('$0.01', str(amt.inverted())) # reverse NUM/DEM
assertEqual('$100.12', str(amt.rounded())) # round it to display precision
assertEqual('$100.12', str(amt.truncated())) # truncate to display precision
assertEqual('$100.00', str(amt.floored())) # floor it to nearest integral
assertEqual('$100.12', str(abs(amt))) # absolute value
assertEqual('$100.12', str(amt)) # render to a string
assertEqual('100.12', amt.quantity_string()) # render quantity to a string
assertEqual('100.12', str(amt.number())) # strip away commodity
assertEqual(1, amt.sign()) # -1, 0 or 1
assert amt.is_nonzero() # True if display amount nonzero
assert not amt.is_zero() # True if display amount is zero
assert not amt.is_realzero() # True only if value is 0/0
assert not amt.is_null() # True if uninitialized
# Amounts can also be converted the standard floats and integers, although
# this is not recommend since it can lose precision.
assertEqual(100.12, amt.to_double())
assert amt.fits_in_long() # there is no `fits_in_double'
assertEqual(100, amt.to_long())
# Finally, amounts can be annotated to provide additional information about
# "lots" of a given commodity. This example shows $100.12 that was purchased
# on 2009/10/01 for 140 EUR. Lot information can be accessed through via the
# Amount's `annotation' property. You can also strip away lot details to get
# the underlying amount. If you want the total price of any Amount, by
# multiplying by its per-unit lot price, call the `Amount.price' method
# instead of the `Annotation.price' property.
amt2 = ledger.Amount('$100.12 {140 EUR} [2009/10/01]')
assert amt2.has_annotation()
assertEqual(amt, amt2.strip_annotations())
assertEqual(ledger.Amount('140 EUR'), amt2.annotation.price)
assertEqual(ledger.Amount('14016,8 EUR'), amt2.price()) # european amount!
###############################################################################
#
# VALUES
#
# As common as Amounts and Balances are, there is a more prevalent numeric
# type you will encounter when generating reports: Value objects. A Value is
# a variadic type that can be any of the following types:
# - Amount
# - Balance
# - boolean
# - integer
# - datetime
# - date
# - string
# - regex
# - sequence
#
# The reason for the variadic type is that it supports dynamic self-promotion.
# For example, it is illegal to add two Amounts of different commodities, but
# it is not illegal to add two Value amounts of different commodities. In the
# former case an exception in raised, but in the latter the Value simply
# promotes itself to a Balance object to make the addition valid.
#
# Values are not used by any of Ledger's data objects (Journal, Transaction,
# Posting or Account), but they are used extensively by value expressions.
val = ledger.Value('$100.00')
assert val.is_amount()
assertEqual('$', val.to_amount().commodity.symbol)
# JOURNALS
#journal.find_account('')
#journal.find_or_create_account('')
# ACCOUNTS
#account.name
#account.fullname()
#account.amount
#account.total
# TRANSACTIONS
#txn.payee
# POSTINGS
#post.account
# REPORTING
#journal.collect('-M food')
#journal.collect_accounts('^assets ^liab ^equity')
print 'Demo completed successfully.'

View file

@ -106,8 +106,6 @@ private:
#endif // HAVE_BOOST_SERIALIZATION #endif // HAVE_BOOST_SERIALIZATION
}; };
shared_ptr<commodity_pool_t> amount_t::current_pool;
bool amount_t::is_initialized = false; bool amount_t::is_initialized = false;
namespace { namespace {
@ -203,7 +201,7 @@ namespace {
} }
} }
void amount_t::initialize(shared_ptr<commodity_pool_t> pool) void amount_t::initialize()
{ {
if (! is_initialized) { if (! is_initialized) {
mpz_init(temp); mpz_init(temp);
@ -211,26 +209,35 @@ void amount_t::initialize(shared_ptr<commodity_pool_t> pool)
mpfr_init(tempf); mpfr_init(tempf);
mpfr_init(tempfb); mpfr_init(tempfb);
commodity_pool_t::current_pool.reset(new commodity_pool_t);
// Add time commodity conversions, so that timelog's may be parsed
// in terms of seconds, but reported as minutes or hours.
if (commodity_t * commodity = commodity_pool_t::current_pool->create("s"))
commodity->add_flags(COMMODITY_BUILTIN | COMMODITY_NOMARKET);
else
assert(false);
// Add a "percentile" commodity
if (commodity_t * commodity = commodity_pool_t::current_pool->create("%"))
commodity->add_flags(COMMODITY_BUILTIN | COMMODITY_NOMARKET);
else
assert(false);
is_initialized = true; is_initialized = true;
} }
current_pool = pool;
}
void amount_t::initialize()
{
initialize(shared_ptr<commodity_pool_t>(new commodity_pool_t));
} }
void amount_t::shutdown() void amount_t::shutdown()
{ {
current_pool.reset();
if (is_initialized) { if (is_initialized) {
mpz_clear(temp); mpz_clear(temp);
mpq_clear(tempq); mpq_clear(tempq);
mpfr_clear(tempf); mpfr_clear(tempf);
mpfr_clear(tempfb); mpfr_clear(tempfb);
commodity_pool_t::current_pool.reset();
is_initialized = false; is_initialized = false;
} }
} }
@ -670,7 +677,7 @@ amount_t::value(const bool primary_only,
if (in_terms_of && commodity() == *in_terms_of) { if (in_terms_of && commodity() == *in_terms_of) {
return *this; return *this;
} }
else if (is_annotated() && annotation().price && else if (has_annotation() && annotation().price &&
annotation().has_flags(ANNOTATION_PRICE_FIXATED)) { annotation().has_flags(ANNOTATION_PRICE_FIXATED)) {
return (*annotation().price * number()).rounded(); return (*annotation().price * number()).rounded();
} }
@ -696,7 +703,7 @@ amount_t::value(const bool primary_only,
amount_t amount_t::price() const amount_t amount_t::price() const
{ {
if (is_annotated() && annotation().price) { if (has_annotation() && annotation().price) {
amount_t temp(*annotation().price); amount_t temp(*annotation().price);
temp *= *this; temp *= *this;
DEBUG("amount.price", "Returning price of " << *this << " = " << temp); DEBUG("amount.price", "Returning price of " << *this << " = " << temp);
@ -776,7 +783,8 @@ bool amount_t::fits_in_long() const
commodity_t& amount_t::commodity() const commodity_t& amount_t::commodity() const
{ {
return has_commodity() ? *commodity_ : *current_pool->null_commodity; return (has_commodity() ?
*commodity_ : *commodity_pool_t::current_pool->null_commodity);
} }
bool amount_t::has_commodity() const bool amount_t::has_commodity() const
@ -794,7 +802,7 @@ void amount_t::annotate(const annotation_t& details)
else if (! has_commodity()) else if (! has_commodity())
return; // ignore attempt to annotate a "bare commodity return; // ignore attempt to annotate a "bare commodity
if (commodity().is_annotated()) { if (commodity().has_annotation()) {
this_ann = &as_annotated_commodity(commodity()); this_ann = &as_annotated_commodity(commodity());
this_base = &this_ann->referent(); this_base = &this_ann->referent();
} else { } else {
@ -816,15 +824,15 @@ void amount_t::annotate(const annotation_t& details)
DEBUG("amounts.commodities", "Annotated amount is " << *this); DEBUG("amounts.commodities", "Annotated amount is " << *this);
} }
bool amount_t::is_annotated() const bool amount_t::has_annotation() const
{ {
if (! quantity) if (! quantity)
throw_(amount_error, throw_(amount_error,
_("Cannot determine if an uninitialized amount's commodity is annotated")); _("Cannot determine if an uninitialized amount's commodity is annotated"));
assert(! has_commodity() || ! commodity().is_annotated() || assert(! has_commodity() || ! commodity().has_annotation() ||
as_annotated_commodity(commodity()).details); as_annotated_commodity(commodity()).details);
return has_commodity() && commodity().is_annotated(); return has_commodity() && commodity().has_annotation();
} }
annotation_t& amount_t::annotation() annotation_t& amount_t::annotation()
@ -833,7 +841,7 @@ annotation_t& amount_t::annotation()
throw_(amount_error, throw_(amount_error,
_("Cannot return commodity annotation details of an uninitialized amount")); _("Cannot return commodity annotation details of an uninitialized amount"));
if (! commodity().is_annotated()) if (! commodity().has_annotation())
throw_(amount_error, throw_(amount_error,
_("Request for annotation details from an unannotated amount")); _("Request for annotation details from an unannotated amount"));
@ -963,15 +971,16 @@ bool amount_t::parse(std::istream& in, const parse_flags_t& flags)
if (symbol.empty()) { if (symbol.empty()) {
commodity_ = NULL; commodity_ = NULL;
} else { } else {
commodity_ = current_pool->find(symbol); commodity_ = commodity_pool_t::current_pool->find(symbol);
if (! commodity_) { if (! commodity_) {
commodity_ = current_pool->create(symbol); commodity_ = commodity_pool_t::current_pool->create(symbol);
newly_created = true; newly_created = true;
} }
assert(commodity_); assert(commodity_);
if (details) if (details)
commodity_ = current_pool->find_or_create(*commodity_, details); commodity_ =
commodity_pool_t::current_pool->find_or_create(*commodity_, details);
} }
// Quickly scan through and verify the correctness of the amount's use of // Quickly scan through and verify the correctness of the amount's use of
@ -1206,7 +1215,6 @@ void to_xml(std::ostream& out, const amount_t& amt, bool commodity_details)
template<class Archive> template<class Archive>
void amount_t::serialize(Archive& ar, const unsigned int /* version */) void amount_t::serialize(Archive& ar, const unsigned int /* version */)
{ {
ar & current_pool;
ar & is_initialized; ar & is_initialized;
ar & quantity; ar & quantity;
ar & commodity_; ar & commodity_;

View file

@ -97,12 +97,8 @@ class amount_t
ordered_field_operators<amount_t, long> > > > ordered_field_operators<amount_t, long> > > >
{ {
public: public:
/** Indicates which commodity pool should be used. */
static shared_ptr<commodity_pool_t> current_pool;
/** Ready the amount subsystem for use. /** Ready the amount subsystem for use.
@note Normally called by session_t::initialize(). */ @note Normally called by session_t::initialize(). */
static void initialize(shared_ptr<commodity_pool_t> pool);
static void initialize(); static void initialize();
/** Shutdown the amount subsystem and free all resources. /** Shutdown the amount subsystem and free all resources.
@note Normally called by session_t::shutdown(). */ @note Normally called by session_t::shutdown(). */
@ -577,7 +573,7 @@ public:
been stripped. been stripped.
*/ */
void annotate(const annotation_t& details); void annotate(const annotation_t& details);
bool is_annotated() const; bool has_annotation() const;
annotation_t& annotation(); annotation_t& annotation();
const annotation_t& annotation() const { const annotation_t& annotation() const {

View file

@ -135,13 +135,13 @@ void annotation_t::print(std::ostream& out, bool keep_base) const
bool keep_details_t::keep_all(const commodity_t& comm) const bool keep_details_t::keep_all(const commodity_t& comm) const
{ {
return (! comm.is_annotated() || return (! comm.has_annotation() ||
(keep_price && keep_date && keep_tag && ! only_actuals)); (keep_price && keep_date && keep_tag && ! only_actuals));
} }
bool keep_details_t::keep_any(const commodity_t& comm) const bool keep_details_t::keep_any(const commodity_t& comm) const
{ {
return comm.is_annotated() && (keep_price || keep_date || keep_tag); return comm.has_annotation() && (keep_price || keep_date || keep_tag);
} }
bool annotated_commodity_t::operator==(const commodity_t& comm) const bool annotated_commodity_t::operator==(const commodity_t& comm) const

View file

@ -43,21 +43,21 @@ balance_t::balance_t(const double val)
{ {
TRACE_CTOR(balance_t, "const double"); TRACE_CTOR(balance_t, "const double");
amounts.insert amounts.insert
(amounts_map::value_type(amount_t::current_pool->null_commodity, val)); (amounts_map::value_type(commodity_pool_t::current_pool->null_commodity, val));
} }
balance_t::balance_t(const unsigned long val) balance_t::balance_t(const unsigned long val)
{ {
TRACE_CTOR(balance_t, "const unsigned long"); TRACE_CTOR(balance_t, "const unsigned long");
amounts.insert amounts.insert
(amounts_map::value_type(amount_t::current_pool->null_commodity, val)); (amounts_map::value_type(commodity_pool_t::current_pool->null_commodity, val));
} }
balance_t::balance_t(const long val) balance_t::balance_t(const long val)
{ {
TRACE_CTOR(balance_t, "const long"); TRACE_CTOR(balance_t, "const long");
amounts.insert amounts.insert
(amounts_map::value_type(amount_t::current_pool->null_commodity, val)); (amounts_map::value_type(commodity_pool_t::current_pool->null_commodity, val));
} }
balance_t& balance_t::operator+=(const balance_t& bal) balance_t& balance_t::operator+=(const balance_t& bal)

View file

@ -600,11 +600,11 @@ bool compare_amount_commodities::operator()(const amount_t * left,
if (cmp != 0) if (cmp != 0)
return cmp < 0; return cmp < 0;
if (! leftcomm.is_annotated()) { if (! leftcomm.has_annotation()) {
return rightcomm.is_annotated(); return rightcomm.has_annotation();
} }
else if (! rightcomm.is_annotated()) { else if (! rightcomm.has_annotation()) {
return ! leftcomm.is_annotated(); return ! leftcomm.has_annotation();
} }
else { else {
annotated_commodity_t& aleftcomm(static_cast<annotated_commodity_t&>(leftcomm)); annotated_commodity_t& aleftcomm(static_cast<annotated_commodity_t&>(leftcomm));
@ -675,7 +675,7 @@ void to_xml(std::ostream& out, const commodity_t& comm,
} }
if (commodity_details) { if (commodity_details) {
if (comm.is_annotated()) if (comm.has_annotation())
to_xml(out, as_annotated_commodity(comm).details); to_xml(out, as_annotated_commodity(comm).details);
if (comm.varied_history()) { if (comm.varied_history()) {

View file

@ -251,7 +251,7 @@ public:
return *this; return *this;
} }
bool is_annotated() const { bool has_annotation() const {
return annotated; return annotated;
} }

View file

@ -77,7 +77,6 @@ journal_t::~journal_t()
checked_delete(xact); checked_delete(xact);
checked_delete(master); checked_delete(master);
commodity_pool.reset();
} }
void journal_t::initialize() void journal_t::initialize()
@ -85,21 +84,6 @@ void journal_t::initialize()
master = new account_t; master = new account_t;
bucket = NULL; bucket = NULL;
was_loaded = false; was_loaded = false;
commodity_pool.reset(new commodity_pool_t);
// Add time commodity conversions, so that timelog's may be parsed
// in terms of seconds, but reported as minutes or hours.
if (commodity_t * commodity = commodity_pool->create("s"))
commodity->add_flags(COMMODITY_BUILTIN | COMMODITY_NOMARKET);
else
assert(false);
// Add a "percentile" commodity
if (commodity_t * commodity = commodity_pool->create("%"))
commodity->add_flags(COMMODITY_BUILTIN | COMMODITY_NOMARKET);
else
assert(false);
} }
void journal_t::add_account(account_t * acct) void journal_t::add_account(account_t * acct)

View file

@ -47,7 +47,6 @@
namespace ledger { namespace ledger {
class commodity_pool_t;
class xact_base_t; class xact_base_t;
class xact_t; class xact_t;
class auto_xact_t; class auto_xact_t;
@ -112,8 +111,6 @@ public:
std::list<fileinfo_t> sources; std::list<fileinfo_t> sources;
bool was_loaded; bool was_loaded;
shared_ptr<commodity_pool_t> commodity_pool;
journal_t(); journal_t();
journal_t(const path& pathname); journal_t(const path& pathname);
journal_t(const string& str); journal_t(const string& str);

View file

@ -624,7 +624,7 @@ bool expr_t::op_t::print(std::ostream& out, const context_t& context) const
} }
if (! symbol.empty()) { if (! symbol.empty()) {
if (amount_t::current_pool->find(symbol)) if (commodity_pool_t::current_pool->find(symbol))
out << '@'; out << '@';
out << symbol; out << symbol;
} }

View file

@ -39,6 +39,8 @@
namespace ledger { namespace ledger {
shared_ptr<commodity_pool_t> commodity_pool_t::current_pool;
commodity_pool_t::commodity_pool_t() commodity_pool_t::commodity_pool_t()
: default_commodity(NULL), keep_base(false), : default_commodity(NULL), keep_base(false),
quote_leeway(86400), get_quotes(false), quote_leeway(86400), get_quotes(false),
@ -318,8 +320,7 @@ optional<price_point_t> commodity_pool_t::parse_price_directive(char * line)
VERIFY(point.price.valid()); VERIFY(point.price.valid());
DEBUG("commodity.download", "Looking up symbol: " << symbol); DEBUG("commodity.download", "Looking up symbol: " << symbol);
if (commodity_t * commodity = if (commodity_t * commodity = find_or_create(symbol)) {
amount_t::current_pool->find_or_create(symbol)) {
DEBUG("commodity.download", "Adding price for " << symbol << ": " DEBUG("commodity.download", "Adding price for " << symbol << ": "
<< point.when << " " << point.price); << point.when << " " << point.price);
commodity->add_price(point.when, point.price, true); commodity->add_price(point.when, point.price, true);

View file

@ -57,6 +57,7 @@ struct cost_breakdown_t
class commodity_pool_t : public noncopyable class commodity_pool_t : public noncopyable
{ {
public:
/** /**
* The commodities collection in commodity_pool_t maintains pointers to all * The commodities collection in commodity_pool_t maintains pointers to all
* the commodities which have ever been created by the user, whether * the commodities which have ever been created by the user, whether
@ -65,7 +66,6 @@ class commodity_pool_t : public noncopyable
*/ */
typedef std::map<string, commodity_t *> commodities_map; typedef std::map<string, commodity_t *> commodities_map;
public:
commodities_map commodities; commodities_map commodities;
commodity_t * null_commodity; commodity_t * null_commodity;
commodity_t * default_commodity; commodity_t * default_commodity;
@ -76,6 +76,8 @@ public:
long quote_leeway; // --leeway= long quote_leeway; // --leeway=
bool get_quotes; // --download bool get_quotes; // --download
static shared_ptr<commodity_pool_t> current_pool;
function<optional<price_point_t> function<optional<price_point_t>
(commodity_t& commodity, const optional<commodity_t&>& in_terms_of)> (commodity_t& commodity, const optional<commodity_t&>& in_terms_of)>
get_commodity_quote; get_commodity_quote;
@ -136,6 +138,7 @@ private:
template<class Archive> template<class Archive>
void serialize(Archive& ar, const unsigned int /* version */) { void serialize(Archive& ar, const unsigned int /* version */) {
ar & current_pool;
ar & commodities; ar & commodities;
ar & null_commodity; ar & null_commodity;
ar & default_commodity; ar & default_commodity;

View file

@ -45,26 +45,16 @@ using namespace boost::python;
namespace { namespace {
boost::optional<amount_t> py_value_0(const amount_t& amount) { boost::optional<amount_t> py_value_0(const amount_t& amount) {
return amount.value(); return amount.value(false, CURRENT_TIME());
} }
boost::optional<amount_t> py_value_1(const amount_t& amount, boost::optional<amount_t> py_value_1(const amount_t& amount,
const bool primary_only) { commodity_t& in_terms_of) {
return amount.value(primary_only); return amount.value(false, CURRENT_TIME(), in_terms_of);
} }
boost::optional<amount_t> py_value_2(const amount_t& amount,
boost::optional<amount_t> commodity_t& in_terms_of,
py_value_2(const amount_t& amount, datetime_t& moment) {
const bool primary_only, return amount.value(false, moment, in_terms_of);
const boost::optional<datetime_t>& moment) {
return amount.value(primary_only, moment);
}
boost::optional<amount_t>
py_value_3(const amount_t& amount,
const bool primary_only,
const boost::optional<datetime_t>& moment,
const boost::optional<commodity_t&>& in_terms_of) {
return amount.value(primary_only, moment, in_terms_of);
} }
void py_parse_2(amount_t& amount, object in, unsigned char flags) { void py_parse_2(amount_t& amount, object in, unsigned char flags) {
@ -97,8 +87,15 @@ namespace {
} }
} }
void py_amount_initialize() { annotation_t& py_amount_annotation(amount_t& amount) {
amount_t::initialize(); return amount.annotation();
}
amount_t py_strip_annotations_0(amount_t& amount) {
return amount.strip_annotations(keep_details_t());
}
amount_t py_strip_annotations_1(amount_t& amount, const keep_details_t& keep) {
return amount.strip_annotations(keep);
} }
} // unnamed namespace } // unnamed namespace
@ -113,11 +110,7 @@ EXC_TRANSLATOR(amount_error)
void export_amount() void export_amount()
{ {
class_< amount_t > ("Amount") class_< amount_t > ("Amount")
.add_static_property("current_pool", .def("initialize", &amount_t::initialize) // only for the PyUnitTests
make_getter(&amount_t::current_pool,
return_internal_reference<>()))
.def("initialize", py_amount_initialize) // only for the PyUnitTests
.staticmethod("initialize") .staticmethod("initialize")
.def("shutdown", &amount_t::shutdown) .def("shutdown", &amount_t::shutdown)
.staticmethod("shutdown") .staticmethod("shutdown")
@ -196,11 +189,11 @@ internal precision."))
.def(self / long()) .def(self / long())
.def(long() / self) .def(long() / self)
.def("precision", &amount_t::precision) .add_property("precision", &amount_t::precision)
.def("keep_precision", &amount_t::keep_precision) .add_property("display_precision", &amount_t::display_precision)
.def("set_keep_precision", &amount_t::set_keep_precision, args("keep"), .add_property("keep_precision",
_("Set whether an amount should always display at full precision.")) &amount_t::keep_precision,
.def("display_precision", &amount_t::display_precision) &amount_t::set_keep_precision)
.def("negated", &amount_t::negated) .def("negated", &amount_t::negated)
.def("in_place_negate", &amount_t::in_place_negate, .def("in_place_negate", &amount_t::in_place_negate,
@ -237,9 +230,8 @@ internal precision."))
return_internal_reference<>()) return_internal_reference<>())
.def("value", py_value_0) .def("value", py_value_0)
.def("value", py_value_1, args("primary_only")) .def("value", py_value_1, args("in_terms_of"))
.def("value", py_value_2, args("primary_only", "moment")) .def("value", py_value_2, args("in_terms_of", "moment"))
.def("value", py_value_3, args("primary_only", "moment", "in_terms_of"))
.def("price", &amount_t::price) .def("price", &amount_t::price)
@ -262,21 +254,23 @@ internal precision."))
.def("__repr__", &amount_t::to_fullstring) .def("__repr__", &amount_t::to_fullstring)
.def("quantity_string", &amount_t::quantity_string) .def("quantity_string", &amount_t::quantity_string)
.def("commodity", &amount_t::commodity, .add_property("commodity",
return_internal_reference<>()) make_function(&amount_t::commodity,
return_value_policy<reference_existing_object>()),
make_function(&amount_t::set_commodity,
with_custodian_and_ward<1, 2>()))
.def("has_commodity", &amount_t::has_commodity) .def("has_commodity", &amount_t::has_commodity)
.def("set_commodity", &amount_t::set_commodity,
with_custodian_and_ward<1, 2>())
.def("clear_commodity", &amount_t::clear_commodity) .def("clear_commodity", &amount_t::clear_commodity)
.def("number", &amount_t::number) .def("number", &amount_t::number)
.def("annotate", &amount_t::annotate) .def("annotate", &amount_t::annotate)
.def("is_annotated", &amount_t::is_annotated) .def("has_annotation", &amount_t::has_annotation)
#if 0 .add_property("annotation",
.def("annotation", &amount_t::annotation) make_function(py_amount_annotation,
#endif return_internal_reference<>()))
.def("strip_annotations", &amount_t::strip_annotations) .def("strip_annotations", py_strip_annotations_0)
.def("strip_annotations", py_strip_annotations_1)
.def("parse", py_parse_1) .def("parse", py_parse_1)
.def("parse", py_parse_2) .def("parse", py_parse_2)

View file

@ -45,26 +45,16 @@ using namespace boost::python;
namespace { namespace {
boost::optional<balance_t> py_value_0(const balance_t& balance) { boost::optional<balance_t> py_value_0(const balance_t& balance) {
return balance.value(); return balance.value(false, CURRENT_TIME());
} }
boost::optional<balance_t> py_value_1(const balance_t& balance, boost::optional<balance_t> py_value_1(const balance_t& balance,
const bool primary_only) { commodity_t& in_terms_of) {
return balance.value(primary_only); return balance.value(false, CURRENT_TIME(), in_terms_of);
} }
boost::optional<balance_t> py_value_2(const balance_t& balance,
boost::optional<balance_t> commodity_t& in_terms_of,
py_value_2(const balance_t& balance, datetime_t& moment) {
const bool primary_only, return balance.value(false, moment, in_terms_of);
const boost::optional<datetime_t>& moment) {
return balance.value(primary_only, moment);
}
boost::optional<balance_t>
py_value_3(const balance_t& balance,
const bool primary_only,
const boost::optional<datetime_t>& moment,
const boost::optional<commodity_t&>& in_terms_of) {
return balance.value(primary_only, moment, in_terms_of);
} }
boost::optional<amount_t> boost::optional<amount_t>
@ -108,6 +98,13 @@ namespace {
return (*elem).second; return (*elem).second;
} }
balance_t py_strip_annotations_0(balance_t& balance) {
return balance.strip_annotations(keep_details_t());
}
balance_t py_strip_annotations_1(balance_t& balance, const keep_details_t& keep) {
return balance.strip_annotations(keep);
}
} // unnamed namespace } // unnamed namespace
#define EXC_TRANSLATOR(type) \ #define EXC_TRANSLATOR(type) \
@ -193,9 +190,8 @@ void export_balance()
return_internal_reference<>()) return_internal_reference<>())
.def("value", py_value_0) .def("value", py_value_0)
.def("value", py_value_1, args("primary_only")) .def("value", py_value_1, args("in_terms_of"))
.def("value", py_value_2, args("primary_only", "moment")) .def("value", py_value_2, args("in_terms_of", "moment"))
.def("value", py_value_3, args("primary_only", "moment", "in_terms_of"))
.def("price", &balance_t::price) .def("price", &balance_t::price)
@ -215,7 +211,8 @@ void export_balance()
.def("number", &balance_t::number) .def("number", &balance_t::number)
.def("strip_annotations", &balance_t::strip_annotations) .def("strip_annotations", py_strip_annotations_0)
.def("strip_annotations", py_strip_annotations_1)
.def("valid", &balance_t::valid) .def("valid", &balance_t::valid)
; ;

View file

@ -32,6 +32,7 @@
#include <system.hh> #include <system.hh>
#include "pyinterp.h" #include "pyinterp.h"
#include "pyutils.h"
#include "commodity.h" #include "commodity.h"
#include "annotate.h" #include "annotate.h"
#include "pool.h" #include "pool.h"
@ -81,6 +82,12 @@ namespace {
// Exchange one commodity for another, while recording the factored price. // Exchange one commodity for another, while recording the factored price.
void py_exchange_2(commodity_pool_t& pool,
commodity_t& commodity,
const amount_t& per_unit_cost)
{
pool.exchange(commodity, per_unit_cost, CURRENT_TIME());
}
void py_exchange_3(commodity_pool_t& pool, void py_exchange_3(commodity_pool_t& pool,
commodity_t& commodity, commodity_t& commodity,
const amount_t& per_unit_cost, const amount_t& per_unit_cost,
@ -99,6 +106,77 @@ namespace {
return pool.exchange(amount, cost, is_per_unit, moment, tag); return pool.exchange(amount, cost, is_per_unit, moment, tag);
} }
commodity_t * py_pool_getitem(commodity_pool_t& pool, const string& symbol)
{
commodity_pool_t::commodities_map::iterator i =
pool.commodities.find(symbol);
if (i == pool.commodities.end()) {
PyErr_SetString(PyExc_ValueError,
(string("Could not find commodity ") + symbol).c_str());
throw boost::python::error_already_set();
}
return (*i).second;
}
python::list py_pool_keys(commodity_pool_t& pool) {
python::list keys;
BOOST_REVERSE_FOREACH
(const commodity_pool_t::commodities_map::value_type& pair,
pool.commodities) {
keys.insert(0, pair.first);
}
return keys;
}
bool py_pool_contains(commodity_pool_t& pool, const string& symbol) {
return pool.commodities.find(symbol) != pool.commodities.end();
}
commodity_pool_t::commodities_map::iterator
py_pool_commodities_begin(commodity_pool_t& pool) {
return pool.commodities.begin();
}
commodity_pool_t::commodities_map::iterator
py_pool_commodities_end(commodity_pool_t& pool) {
return pool.commodities.end();
}
typedef transform_iterator
<function<string(commodity_pool_t::commodities_map::value_type&)>,
commodity_pool_t::commodities_map::iterator>
commodities_map_firsts_iterator;
commodities_map_firsts_iterator
py_pool_commodities_keys_begin(commodity_pool_t& pool) {
return make_transform_iterator
(pool.commodities.begin(),
bind(&commodity_pool_t::commodities_map::value_type::first, _1));
}
commodities_map_firsts_iterator
py_pool_commodities_keys_end(commodity_pool_t& pool) {
return make_transform_iterator
(pool.commodities.end(),
bind(&commodity_pool_t::commodities_map::value_type::first, _1));
}
typedef transform_iterator
<function<commodity_t *(commodity_pool_t::commodities_map::value_type&)>,
commodity_pool_t::commodities_map::iterator>
commodities_map_seconds_iterator;
commodities_map_seconds_iterator
py_pool_commodities_values_begin(commodity_pool_t& pool) {
return make_transform_iterator
(pool.commodities.begin(),
bind(&commodity_pool_t::commodities_map::value_type::second, _1));
}
commodities_map_seconds_iterator
py_pool_commodities_values_end(commodity_pool_t& pool) {
return make_transform_iterator
(pool.commodities.end(),
bind(&commodity_pool_t::commodities_map::value_type::second, _1));
}
void py_add_price_2(commodity_t& commodity, void py_add_price_2(commodity_t& commodity,
const datetime_t& date, const amount_t& price) { const datetime_t& date, const amount_t& price) {
commodity.add_price(date, price); commodity.add_price(date, price);
@ -123,16 +201,46 @@ namespace {
return details.keep_any(comm); return details.keep_any(comm);
} }
commodity_t& py_commodity_referent(commodity_t& comm) {
return comm.referent();
}
commodity_t& py_annotated_commodity_referent(annotated_commodity_t& comm) {
return comm.referent();
}
commodity_t& py_strip_annotations_0(commodity_t& comm) {
return comm.strip_annotations(keep_details_t());
}
commodity_t& py_strip_annotations_1(commodity_t& comm,
const keep_details_t& keep) {
return comm.strip_annotations(keep);
}
commodity_t& py_strip_ann_annotations_0(annotated_commodity_t& comm) {
return comm.strip_annotations(keep_details_t());
}
commodity_t& py_strip_ann_annotations_1(annotated_commodity_t& comm,
const keep_details_t& keep) {
return comm.strip_annotations(keep);
}
boost::optional<amount_t> py_price(annotation_t& ann) {
return ann.price;
}
boost::optional<amount_t> py_set_price(annotation_t& ann,
const boost::optional<amount_t>& price) {
return ann.price = price;
}
} // unnamed namespace } // unnamed namespace
void export_commodity() void export_commodity()
{ {
class_< commodity_pool_t, boost::noncopyable > ("CommodityPool", no_init) class_< commodity_pool_t, shared_ptr<commodity_pool_t>,
boost::noncopyable > ("CommodityPool", no_init)
.add_property("null_commodity", .add_property("null_commodity",
make_getter(&commodity_pool_t::null_commodity, make_getter(&commodity_pool_t::null_commodity,
return_internal_reference<>()), return_internal_reference<>()))
make_setter(&commodity_pool_t::null_commodity,
with_custodian_and_ward<1, 2>()))
.add_property("default_commodity", .add_property("default_commodity",
make_getter(&commodity_pool_t::default_commodity, make_getter(&commodity_pool_t::default_commodity,
return_internal_reference<>()), return_internal_reference<>()),
@ -157,25 +265,46 @@ void export_commodity()
.def("make_qualified_name", &commodity_pool_t::make_qualified_name) .def("make_qualified_name", &commodity_pool_t::make_qualified_name)
.def("create", py_create_1, return_internal_reference<>()) .def("create", py_create_1,
.def("create", py_create_2, return_internal_reference<>()) return_value_policy<reference_existing_object>())
.def("create", py_create_2,
return_value_policy<reference_existing_object>())
.def("find_or_create", py_find_or_create_1, .def("find_or_create", py_find_or_create_1,
return_internal_reference<>()) return_value_policy<reference_existing_object>())
.def("find_or_create", py_find_or_create_2, .def("find_or_create", py_find_or_create_2,
return_internal_reference<>()) return_value_policy<reference_existing_object>())
.def("find", py_find_1, return_internal_reference<>()) .def("find", py_find_1, return_value_policy<reference_existing_object>())
.def("find", py_find_2, return_internal_reference<>()) .def("find", py_find_2, return_value_policy<reference_existing_object>())
.def("exchange", py_exchange_2, with_custodian_and_ward<1, 2>())
.def("exchange", py_exchange_3, with_custodian_and_ward<1, 2>()) .def("exchange", py_exchange_3, with_custodian_and_ward<1, 2>())
.def("exchange", py_exchange_5) .def("exchange", py_exchange_5)
.def("parse_price_directive", &commodity_pool_t::parse_price_directive) .def("parse_price_directive", &commodity_pool_t::parse_price_directive)
.def("parse_price_expression", &commodity_pool_t::parse_price_expression, .def("parse_price_expression", &commodity_pool_t::parse_price_expression,
return_internal_reference<>()) return_value_policy<reference_existing_object>())
.def("__getitem__", py_pool_getitem,
return_value_policy<reference_existing_object>())
.def("keys", py_pool_keys)
.def("has_key", py_pool_contains)
.def("__contains__", py_pool_contains)
.def("__iter__", range<return_value_policy<reference_existing_object> >
(py_pool_commodities_begin, py_pool_commodities_end))
.def("iteritems", range<return_value_policy<reference_existing_object> >
(py_pool_commodities_begin, py_pool_commodities_end))
.def("iterkeys", range<>(py_pool_commodities_keys_begin,
py_pool_commodities_keys_end))
.def("itervalues", range<return_value_policy<reference_existing_object> >
(py_pool_commodities_values_begin, py_pool_commodities_values_end))
; ;
map_value_type_converter<commodity_pool_t::commodities_map>();
scope().attr("commodities") = commodity_pool_t::current_pool;
scope().attr("COMMODITY_STYLE_DEFAULTS") = COMMODITY_STYLE_DEFAULTS; scope().attr("COMMODITY_STYLE_DEFAULTS") = COMMODITY_STYLE_DEFAULTS;
scope().attr("COMMODITY_STYLE_SUFFIXED") = COMMODITY_STYLE_SUFFIXED; scope().attr("COMMODITY_STYLE_SUFFIXED") = COMMODITY_STYLE_SUFFIXED;
scope().attr("COMMODITY_STYLE_SEPARATED") = COMMODITY_STYLE_SEPARATED; scope().attr("COMMODITY_STYLE_SEPARATED") = COMMODITY_STYLE_SEPARATED;
@ -209,33 +338,30 @@ void export_commodity()
.def("symbol_needs_quotes", &commodity_t::symbol_needs_quotes) .def("symbol_needs_quotes", &commodity_t::symbol_needs_quotes)
.staticmethod("symbol_needs_quotes") .staticmethod("symbol_needs_quotes")
#if 0 .add_property("referent",
.def("referent", &commodity_t::referent, make_function(py_commodity_referent,
return_internal_reference<>()) return_value_policy<reference_existing_object>()))
#endif
.def("is_annotated", &commodity_t::is_annotated) .def("has_annotation", &commodity_t::has_annotation)
.def("strip_annotations", &commodity_t::strip_annotations, .def("strip_annotations", py_strip_annotations_0,
return_internal_reference<>()) return_value_policy<reference_existing_object>())
.def("strip_annotations", py_strip_annotations_1,
return_value_policy<reference_existing_object>())
.def("write_annotations", &commodity_t::write_annotations) .def("write_annotations", &commodity_t::write_annotations)
.def("pool", &commodity_t::pool, .def("pool", &commodity_t::pool,
return_internal_reference<>()) return_value_policy<reference_existing_object>())
.def("base_symbol", &commodity_t::base_symbol) .add_property("base_symbol", &commodity_t::base_symbol)
.def("symbol", &commodity_t::symbol) .add_property("symbol", &commodity_t::symbol)
.def("mapping_key", &commodity_t::mapping_key) .add_property("mapping_key", &commodity_t::mapping_key)
.def("name", &commodity_t::name) .add_property("name", &commodity_t::name, &commodity_t::set_name)
.def("set_name", &commodity_t::set_name) .add_property("note", &commodity_t::note, &commodity_t::set_note)
.def("note", &commodity_t::note) .add_property("precision", &commodity_t::precision,
.def("set_note", &commodity_t::set_note) &commodity_t::set_precision)
.def("precision", &commodity_t::precision) .add_property("smaller", &commodity_t::smaller, &commodity_t::set_smaller)
.def("set_precision", &commodity_t::set_precision) .add_property("larger", &commodity_t::larger, &commodity_t::set_larger)
.def("smaller", &commodity_t::smaller)
.def("set_smaller", &commodity_t::set_smaller)
.def("larger", &commodity_t::larger)
.def("set_larger", &commodity_t::set_larger)
.def("add_price", py_add_price_2) .def("add_price", py_add_price_2)
.def("add_price", py_add_price_3) .def("add_price", py_add_price_3)
@ -257,9 +383,7 @@ void export_commodity()
.def("drop_flags", &supports_flags<>::drop_flags) .def("drop_flags", &supports_flags<>::drop_flags)
#endif #endif
.add_property("price", .add_property("price", py_price, py_set_price)
make_getter(&annotation_t::price),
make_setter(&annotation_t::price))
.add_property("date", .add_property("date",
make_getter(&annotation_t::date), make_getter(&annotation_t::date),
make_setter(&annotation_t::date)) make_setter(&annotation_t::date))
@ -306,13 +430,14 @@ void export_commodity()
.def(self == self) .def(self == self)
.def(self == other<commodity_t>()) .def(self == other<commodity_t>())
#if 0 .add_property("referent",
.def("referent", &annotated_commodity_t::referent, make_function(py_annotated_commodity_referent,
return_internal_reference<>()) return_value_policy<reference_existing_object>()))
#endif
.def("strip_annotations", &annotated_commodity_t::strip_annotations, .def("strip_annotations", py_strip_ann_annotations_0,
return_internal_reference<>()) return_value_policy<reference_existing_object>())
.def("strip_annotations", py_strip_ann_annotations_1,
return_value_policy<reference_existing_object>())
.def("write_annotations", &annotated_commodity_t::write_annotations) .def("write_annotations", &annotated_commodity_t::write_annotations)
; ;
} }

View file

@ -52,10 +52,6 @@ namespace {
return *journal.master; return *journal.master;
} }
commodity_pool_t& py_commodity_pool(journal_t& journal) {
return *journal.commodity_pool;
}
long xacts_len(journal_t& journal) long xacts_len(journal_t& journal)
{ {
return journal.xacts.size(); return journal.xacts.size();
@ -276,10 +272,6 @@ void export_journal()
with_custodian_and_ward_postcall<1, 0> >()), with_custodian_and_ward_postcall<1, 0> >()),
make_setter(&journal_t::bucket)) make_setter(&journal_t::bucket))
.add_property("was_loaded", make_getter(&journal_t::was_loaded)) .add_property("was_loaded", make_getter(&journal_t::was_loaded))
.add_property("commodity_pool",
make_getter(&journal_t::commodity_pool,
return_internal_reference<1,
with_custodian_and_ward_postcall<1, 0> >()))
.def("add_account", &journal_t::add_account) .def("add_account", &journal_t::add_account)
.def("remove_account", &journal_t::remove_account) .def("remove_account", &journal_t::remove_account)

View file

@ -47,7 +47,21 @@ BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(set_string_overloads, set_string, 0, 2)
namespace { namespace {
PyObject * py_base_type(value_t& value) { boost::optional<value_t> py_value_0(const value_t& value) {
return value.value(false, CURRENT_TIME());
}
boost::optional<value_t> py_value_1(const value_t& value,
commodity_t& in_terms_of) {
return value.value(false, CURRENT_TIME(), in_terms_of);
}
boost::optional<value_t> py_value_2(const value_t& value,
commodity_t& in_terms_of,
datetime_t& moment) {
return value.value(false, moment, in_terms_of);
}
PyObject * py_base_type(value_t& value)
{
if (value.is_boolean()) { if (value.is_boolean()) {
return (PyObject *)&PyBool_Type; return (PyObject *)&PyBool_Type;
} }
@ -63,16 +77,6 @@ namespace {
} }
} }
expr_t py_value_getattr(const value_t& value, const string& name)
{
if (value.is_scope()) {
if (scope_t * scope = value.as_scope())
return expr_t(scope->lookup(symbol_t::FUNCTION, name), scope);
}
throw_(value_error, _("Cannot lookup attributes in %1") << value.label());
return expr_t();
}
string py_dump(const value_t& value) { string py_dump(const value_t& value) {
std::ostringstream buf; std::ostringstream buf;
value.dump(buf); value.dump(buf);
@ -85,10 +89,20 @@ namespace {
return buf.str(); return buf.str();
} }
void py_set_string(value_t& amount, const string& str) { void py_set_string(value_t& value, const string& str) {
return amount.set_string(str); return value.set_string(str);
} }
annotation_t& py_value_annotation(value_t& value) {
return value.annotation();
}
value_t py_strip_annotations_0(value_t& value) {
return value.strip_annotations(keep_details_t());
}
value_t py_strip_annotations_1(value_t& value, const keep_details_t& keep) {
return value.strip_annotations(keep);
}
} // unnamed namespace } // unnamed namespace
#define EXC_TRANSLATOR(type) \ #define EXC_TRANSLATOR(type) \
@ -243,6 +257,10 @@ void export_value()
.def("unreduced", &value_t::unreduced) .def("unreduced", &value_t::unreduced)
.def("in_place_unreduce", &value_t::in_place_unreduce) .def("in_place_unreduce", &value_t::in_place_unreduce)
.def("value", py_value_0)
.def("value", py_value_1, args("in_terms_of"))
.def("value", py_value_2, args("in_terms_of", "moment"))
.def("value", &value_t::value, value_overloads()) .def("value", &value_t::value, value_overloads())
.def("price", &value_t::price) .def("price", &value_t::price)
.def("exchange_commodities", &value_t::exchange_commodities, .def("exchange_commodities", &value_t::exchange_commodities,
@ -306,16 +324,16 @@ void export_value()
.def("number", &value_t::number) .def("number", &value_t::number)
.def("annotate", &value_t::annotate) .def("annotate", &value_t::annotate)
.def("is_annotated", &value_t::is_annotated) .def("has_annotation", &value_t::has_annotation)
#if 0 .add_property("annotation",
.def("annotation", &value_t::annotation) make_function(py_value_annotation,
#endif return_internal_reference<>()))
.def("strip_annotations", &value_t::strip_annotations) .def("strip_annotations", py_strip_annotations_0)
.def("strip_annotations", py_strip_annotations_1)
#if 0 #if 0
.def("__getitem__", &value_t::operator[]) .def("__getitem__", &value_t::operator[])
#endif #endif
.def("__getattr__", py_value_getattr)
.def("push_back", &value_t::push_back) .def("push_back", &value_t::push_back)
.def("pop_back", &value_t::pop_back) .def("pop_back", &value_t::pop_back)
.def("size", &value_t::size) .def("size", &value_t::size)

View file

@ -106,6 +106,26 @@ struct register_optional_to_python : public boost::noncopyable
} }
}; };
template <typename T1, typename T2>
struct PairToTupleConverter
{
static PyObject * convert(const std::pair<T1, T2>& pair) {
return boost::python::incref
(boost::python::make_tuple(pair.first, pair.second).ptr());
}
};
template <typename MapType>
struct map_value_type_converter
{
map_value_type_converter() {
boost::python::to_python_converter
<typename MapType::value_type,
PairToTupleConverter<const typename MapType::key_type,
typename MapType::mapped_type> >();
}
};
namespace boost { namespace python { namespace boost { namespace python {
// Use expr to create the PyObject corresponding to x // Use expr to create the PyObject corresponding to x

View file

@ -76,13 +76,13 @@ commodity_quote_from_script(commodity_t& commodity,
DEBUG("commodity.download", "downloaded quote: " << buf); DEBUG("commodity.download", "downloaded quote: " << buf);
if (optional<price_point_t> point = if (optional<price_point_t> point =
amount_t::current_pool->parse_price_directive(buf)) { commodity_pool_t::current_pool->parse_price_directive(buf)) {
if (amount_t::current_pool->price_db) { if (commodity_pool_t::current_pool->price_db) {
#if defined(__GNUG__) && __GNUG__ < 3 #if defined(__GNUG__) && __GNUG__ < 3
ofstream database(*amount_t::current_pool->price_db, ofstream database(*commodity_pool_t::current_pool->price_db,
ios::out | ios::app); ios::out | ios::app);
#else #else
ofstream database(*amount_t::current_pool->price_db, ofstream database(*commodity_pool_t::current_pool->price_db,
std::ios_base::out | std::ios_base::app); std::ios_base::out | std::ios_base::app);
#endif #endif
database << "P " database << "P "

View file

@ -69,18 +69,17 @@ void report_t::normalize_options(const string& verb)
item_t::use_effective_date = (HANDLED(effective) && item_t::use_effective_date = (HANDLED(effective) &&
! HANDLED(actual_dates)); ! HANDLED(actual_dates));
session.journal->commodity_pool->keep_base = HANDLED(base); commodity_pool_t::current_pool->keep_base = HANDLED(base);
session.journal->commodity_pool->get_quotes = session.HANDLED(download); commodity_pool_t::current_pool->get_quotes = session.HANDLED(download);
if (session.HANDLED(price_exp_)) if (session.HANDLED(price_exp_))
session.journal->commodity_pool->quote_leeway = commodity_pool_t::current_pool->quote_leeway =
session.HANDLER(price_exp_).value.as_long(); session.HANDLER(price_exp_).value.as_long();
if (session.HANDLED(price_db_)) if (session.HANDLED(price_db_))
session.journal->commodity_pool->price_db = commodity_pool_t::current_pool->price_db = session.HANDLER(price_db_).str();
session.HANDLER(price_db_).str();
else else
session.journal->commodity_pool->price_db = none; commodity_pool_t::current_pool->price_db = none;
if (HANDLED(date_format_)) if (HANDLED(date_format_))
set_date_format(HANDLER(date_format_).str().c_str()); set_date_format(HANDLER(date_format_).str().c_str());
@ -522,7 +521,7 @@ value_t report_t::fn_price(call_scope_t& scope)
value_t report_t::fn_lot_date(call_scope_t& scope) value_t report_t::fn_lot_date(call_scope_t& scope)
{ {
interactive_t args(scope, "v"); interactive_t args(scope, "v");
if (args.value_at(0).is_annotated()) { if (args.value_at(0).has_annotation()) {
const annotation_t& details(args.value_at(0).annotation()); const annotation_t& details(args.value_at(0).annotation());
if (details.date) if (details.date)
return *details.date; return *details.date;
@ -533,7 +532,7 @@ value_t report_t::fn_lot_date(call_scope_t& scope)
value_t report_t::fn_lot_price(call_scope_t& scope) value_t report_t::fn_lot_price(call_scope_t& scope)
{ {
interactive_t args(scope, "v"); interactive_t args(scope, "v");
if (args.value_at(0).is_annotated()) { if (args.value_at(0).has_annotation()) {
const annotation_t& details(args.value_at(0).annotation()); const annotation_t& details(args.value_at(0).annotation());
if (details.price) if (details.price)
return *details.price; return *details.price;
@ -544,7 +543,7 @@ value_t report_t::fn_lot_price(call_scope_t& scope)
value_t report_t::fn_lot_tag(call_scope_t& scope) value_t report_t::fn_lot_tag(call_scope_t& scope)
{ {
interactive_t args(scope, "v"); interactive_t args(scope, "v");
if (args.value_at(0).is_annotated()) { if (args.value_at(0).has_annotation()) {
const annotation_t& details(args.value_at(0).annotation()); const annotation_t& details(args.value_at(0).annotation());
if (details.tag) if (details.tag)
return string_value(*details.tag); return string_value(*details.tag);

View file

@ -45,7 +45,7 @@ void set_session_context(session_t * session)
{ {
if (session) { if (session) {
times_initialize(); times_initialize();
amount_t::initialize(session->journal->commodity_pool); amount_t::initialize();
amount_t::parse_conversion("1.0m", "60s"); amount_t::parse_conversion("1.0m", "60s");
amount_t::parse_conversion("1.0h", "60m"); amount_t::parse_conversion("1.0h", "60m");
@ -179,7 +179,7 @@ void session_t::close_journal_files()
amount_t::shutdown(); amount_t::shutdown();
journal.reset(new journal_t); journal.reset(new journal_t);
amount_t::initialize(journal->commodity_pool); amount_t::initialize();
} }
option_t<session_t> * session_t::lookup_option(const char * p) option_t<session_t> * session_t::lookup_option(const char * p)

View file

@ -490,7 +490,7 @@ void instance_t::default_commodity_directive(char * line)
{ {
amount_t amt(skip_ws(line + 1)); amount_t amt(skip_ws(line + 1));
VERIFY(amt.valid()); VERIFY(amt.valid());
amount_t::current_pool->default_commodity = &amt.commodity(); commodity_pool_t::current_pool->default_commodity = &amt.commodity();
amt.commodity().add_flags(COMMODITY_KNOWN); amt.commodity().add_flags(COMMODITY_KNOWN);
} }
@ -511,7 +511,7 @@ void instance_t::price_conversion_directive(char * line)
void instance_t::price_xact_directive(char * line) void instance_t::price_xact_directive(char * line)
{ {
optional<price_point_t> point = optional<price_point_t> point =
amount_t::current_pool->parse_price_directive(skip_ws(line + 1)); commodity_pool_t::current_pool->parse_price_directive(skip_ws(line + 1));
if (! point) if (! point)
throw parse_error(_("Pricing entry failed to parse")); throw parse_error(_("Pricing entry failed to parse"));
} }
@ -523,7 +523,7 @@ void instance_t::nomarket_directive(char * line)
commodity_t::parse_symbol(p, symbol); commodity_t::parse_symbol(p, symbol);
if (commodity_t * commodity = if (commodity_t * commodity =
amount_t::current_pool->find_or_create(symbol)) commodity_pool_t::current_pool->find_or_create(symbol))
commodity->add_flags(COMMODITY_NOMARKET | COMMODITY_KNOWN); commodity->add_flags(COMMODITY_NOMARKET | COMMODITY_KNOWN);
} }

View file

@ -1321,10 +1321,13 @@ date_parser_t::lexer_t::token_t date_parser_t::lexer_t::next_token()
catch (...) {} catch (...) {}
} }
start = begin;
string term; string term;
bool alnum = std::isalnum(*begin); bool alnum = std::isalnum(*begin);
for (start = begin; (begin != end && ! std::isspace(*begin) && for (; (begin != end && ! std::isspace(*begin) &&
alnum == std::isalnum(*begin)); begin++) ((alnum && static_cast<bool>(std::isalnum(*begin))) ||
(! alnum && ! static_cast<bool>(std::isalnum(*begin))))); begin++)
term.push_back(*begin); term.push_back(*begin);
if (! term.empty()) { if (! term.empty()) {

View file

@ -1353,7 +1353,8 @@ value_t value_t::exchange_commodities(const std::string& commodities,
p; p;
p = std::strtok(NULL, ",")) { p = std::strtok(NULL, ",")) {
if (commodity_t * commodity = if (commodity_t * commodity =
amount_t::current_pool->parse_price_expression(p, add_prices, moment)) { commodity_pool_t::current_pool->parse_price_expression(p, add_prices,
moment)) {
value_t result = value(false, moment, *commodity); value_t result = value(false, moment, *commodity);
if (! result.is_null()) if (! result.is_null())
return result; return result;
@ -1523,10 +1524,10 @@ void value_t::annotate(const annotation_t& details)
throw_(value_error, _("Cannot annotate %1") << label()); throw_(value_error, _("Cannot annotate %1") << label());
} }
bool value_t::is_annotated() const bool value_t::has_annotation() const
{ {
if (is_amount()) if (is_amount())
return as_amount().is_annotated(); return as_amount().has_annotation();
else else
throw_(value_error, throw_(value_error,
_("Cannot determine whether %1 is annotated") << label()); _("Cannot determine whether %1 is annotated") << label());

View file

@ -774,7 +774,7 @@ public:
* Annotated commodity methods. * Annotated commodity methods.
*/ */
void annotate(const annotation_t& details); void annotate(const annotation_t& details);
bool is_annotated() const; bool has_annotation() const;
annotation_t& annotation(); annotation_t& annotation();
const annotation_t& annotation() const { const annotation_t& annotation() const {

View file

@ -122,7 +122,7 @@ bool xact_base_t::finalize()
amount_t& p(post->cost ? *post->cost : post->amount); amount_t& p(post->cost ? *post->cost : post->amount);
if (! p.is_null()) { if (! p.is_null()) {
DEBUG("xact.finalize", "post must balance = " << p.reduced()); DEBUG("xact.finalize", "post must balance = " << p.reduced());
if (! post->cost && post->amount.is_annotated() && if (! post->cost && post->amount.has_annotation() &&
post->amount.annotation().price) { post->amount.annotation().price) {
// If the amount has no cost, but is annotated with a per-unit // If the amount has no cost, but is annotated with a per-unit
// price, use the price times the amount as the cost // price, use the price times the amount as the cost
@ -221,7 +221,7 @@ bool xact_base_t::finalize()
foreach (post_t * post, posts) { foreach (post_t * post, posts) {
if (! post->amount.is_null()) { if (! post->amount.is_null()) {
if (post->amount.is_annotated()) if (post->amount.has_annotation())
top_post = post; top_post = post;
else if (! top_post) else if (! top_post)
top_post = post; top_post = post;
@ -260,7 +260,7 @@ bool xact_base_t::finalize()
foreach (post_t * post, posts) { foreach (post_t * post, posts) {
if (post != top_post && post->must_balance() && if (post != top_post && post->must_balance() &&
! post->amount.is_null() && ! post->amount.is_null() &&
post->amount.is_annotated() && post->amount.has_annotation() &&
post->amount.annotation().price) { post->amount.annotation().price) {
amount_t temp = *post->amount.annotation().price * post->amount; amount_t temp = *post->amount.annotation().price * post->amount;
if (total_cost.is_null()) { if (total_cost.is_null()) {
@ -309,10 +309,11 @@ bool xact_base_t::finalize()
_("A posting's cost must be of a different commodity than its amount")); _("A posting's cost must be of a different commodity than its amount"));
cost_breakdown_t breakdown = cost_breakdown_t breakdown =
amount_t::current_pool->exchange(post->amount, *post->cost, false, commodity_pool_t::current_pool->exchange
datetime_t(date(), time_duration(0, 0, 0, 0))); (post->amount, *post->cost, false,
datetime_t(date(), time_duration(0, 0, 0, 0)));
if (post->amount.is_annotated() && if (post->amount.has_annotation() &&
breakdown.basis_cost.commodity() == breakdown.basis_cost.commodity() ==
breakdown.final_cost.commodity()) { breakdown.final_cost.commodity()) {
if (amount_t gain_loss = (breakdown.basis_cost - if (amount_t gain_loss = (breakdown.basis_cost -

View file

@ -0,0 +1,91 @@
reg
<<<
; -*- ledger -*-
N $
= /^Expenses:Books/
(Liabilities:Taxes) -0.10
~ Monthly
Assets:Bank:Checking $500.00
Income:Salary
~ Yearly
Expenses:Donations $100.00
Assets:Bank:Checking
2004/05/01 * Checking balance
Assets:Bank:Checking $1,000.00
Equity:Opening Balances
2004/05/03=2004/05/01 * Investment balance
Assets:Brokerage 50 AAPL @ $30.00
Equity:Opening Balances
2004/05/14 * Páy dày
Assets:Bank:Checking 500.00€
Income:Salary
2004/05/14 * Another dày in which there is Páying
Asséts:Bánk:Chécking:Asséts:Bánk:Chécking $500.00
Income:Salary
2004/05/14 * Another dày in which there is Páying
Русский язык:Активы:Русский язык:Русский язык $1000.00
Income:Salary
tag foo
2004/05/27 Book Store
Expenses:Books $20.00
Expenses:Cards $40.00
Expenses:Docs $30.00
Liabilities:MasterCard
end tag
2004/05/27 (100) Credit card company
; This is an xact note!
; Sample: Value
Liabilities:MasterCard $20.00
; This is a posting note!
; Sample: Another Value
; :MyTag:
Assets:Bank:Checking
; :AnotherTag:
>>>1
04-May-01 Checking balance Assets:Bank:Checking $1,000.00 $1,000.00
Eq:Opening Balances $-1,000.00 0
04-May-03 Investment balance Assets:Brokerage 50 AAPL 50 AAPL
Eq:Opening Balances $-1,500.00 $-1,500.00
50 AAPL
04-May-14 Páy dày Assets:Bank:Checking 500.00€ $-1,500.00
50 AAPL
500.00€
Income:Salary -500.00€ $-1,500.00
50 AAPL
04-May-14 Another dày in whic.. ..Bá:Ch:As:Bá:Chécking $500.00 $-1,000.00
50 AAPL
Income:Salary $-500.00 $-1,500.00
50 AAPL
04-May-14 Another dày in whic.. Ру:Ак:Ру:Русский язык $1,000.00 $-500.00
50 AAPL
Income:Salary $-1,000.00 $-1,500.00
50 AAPL
04-May-27 Book Store Expenses:Books $20.00 $-1,480.00
50 AAPL
Expenses:Cards $40.00 $-1,440.00
50 AAPL
Expenses:Docs $30.00 $-1,410.00
50 AAPL
Liabilities:MasterCard $-90.00 $-1,500.00
50 AAPL
(Liabilities:Taxes) $-2.00 $-1,502.00
50 AAPL
04-May-27 Credit card company Liabilities:MasterCard $20.00 $-1,482.00
50 AAPL
Assets:Bank:Checking $-20.00 $-1,502.00
50 AAPL
>>>2
=== 0

View file

@ -158,6 +158,8 @@ for line in fd.readlines():
line = re.sub('false', 'False', line) line = re.sub('false', 'False', line)
line = re.sub('CURRENT_TIME\(\)', 'datetime.now()', line) line = re.sub('CURRENT_TIME\(\)', 'datetime.now()', line)
line = re.sub('CURRENT_DATE\(\)', 'date.today()', line) line = re.sub('CURRENT_DATE\(\)', 'date.today()', line)
line = re.sub('commodity\(\)', 'commodity', line)
line = re.sub('precision\(\)', 'precision', line)
line = re.sub('([0-9]+)[FL]', '\\1', line) line = re.sub('([0-9]+)[FL]', '\\1', line)
line = re.sub('([0-9]+)UL', '\\1L', line) line = re.sub('([0-9]+)UL', '\\1L', line)
line = re.sub(';', '', line) line = re.sub(';', '', line)

View file

@ -1,42 +1,52 @@
; -*- ledger -*-
N $ N $
= account =~ /^Expenses:Books/ = /^Expenses:Books/
(Liabilities:Taxes) -0.10 (Liabilities:Taxes) -0.10
~ Monthly ~ Monthly
Assets:Bank:Checking $500.00 Assets:Bank:Checking $500.00
Income:Salary Income:Salary
~ Yearly
Expenses:Donations $100.00
Assets:Bank:Checking
2004/05/01 * Checking balance 2004/05/01 * Checking balance
Assets:Bank:Checking $1,000.00 Assets:Bank:Checking $1,000.00
Equity:Opening Balances Equity:Opening Balances
2004/05/03=2004/05/01 * Investment balance 2004/05/03=2004/05/01 * Investment balance
Assets:Brokerage 50 AAPL @ $30.00 Assets:Brokerage 50 AAPL @ $30.00
Equity:Opening Balances Equity:Opening Balances
2004/05/14 * Páy dày 2004/05/14 * Páy dày
Assets:Bank:Checking 500.00€ Assets:Bank:Checking 500.00€
Income:Salary Income:Salary
2004/05/14 * Another dày in which there is Páying 2004/05/14 * Another dày in which there is Páying
Asséts:Bánk:Chécking:Asséts:Bánk:Chécking $500.00 Asséts:Bánk:Chécking:Asséts:Bánk:Chécking $500.00
Income:Salary Income:Salary
2004/05/14 * Another dày in which there is Páying 2004/05/14 * Another dày in which there is Páying
Русский язык:Русский язык:Русский язык:Русский язык $1000.00 Русский язык:Активы:Русский язык:Русский язык $1000.00
Income:Salary Income:Salary
tag foo
2004/05/27 Book Store 2004/05/27 Book Store
Expenses:Books $20.00 Expenses:Books $20.00
Expenses:Cards $40.00 Expenses:Cards $40.00
Expenses:Docs $30.00 Expenses:Docs $30.00
Liabilities:MasterCard Liabilities:MasterCard
end tag
2004/05/27 (100) Credit card company 2004/05/27 (100) Credit card company
; This is an xact note! ; This is an xact note!
; Sample: Value ; Sample: Value
Liabilities:MasterCard $20.00 Liabilities:MasterCard $20.00
; This is a posting note! ; This is a posting note!
; Sample: Another Value ; Sample: Another Value
; :MyTag: ; :MyTag:

View file

@ -4,16 +4,16 @@
>>>2 >>>2
While parsing file "$sourcepath/src/amount.h", line 67: While parsing file "$sourcepath/src/amount.h", line 67:
Error: No quantity specified for amount Error: No quantity specified for amount
While parsing file "$sourcepath/src/amount.h", line 721: While parsing file "$sourcepath/src/amount.h", line 717:
Error: Invalid date/time: line amount_t amoun Error: Invalid date/time: line amount_t amoun
While parsing file "$sourcepath/src/amount.h", line 727: While parsing file "$sourcepath/src/amount.h", line 723:
Error: Invalid date/time: line string amount_ Error: Invalid date/time: line string amount_
While parsing file "$sourcepath/src/amount.h", line 733: While parsing file "$sourcepath/src/amount.h", line 729:
Error: Invalid date/time: line string amount_ Error: Invalid date/time: line string amount_
While parsing file "$sourcepath/src/amount.h", line 739: While parsing file "$sourcepath/src/amount.h", line 735:
Error: Invalid date/time: line string amount_ Error: Invalid date/time: line string amount_
While parsing file "$sourcepath/src/amount.h", line 745: While parsing file "$sourcepath/src/amount.h", line 741:
Error: Invalid date/time: line std::ostream& Error: Invalid date/time: line std::ostream&
While parsing file "$sourcepath/src/amount.h", line 752: While parsing file "$sourcepath/src/amount.h", line 748:
Error: Invalid date/time: line std::istream& Error: Invalid date/time: line std::istream&
=== 7 === 7