Merge branch 'next'
This commit is contained in:
commit
117dddabd4
33 changed files with 922 additions and 301 deletions
|
|
@ -45,17 +45,24 @@ much further with those.
|
|||
|
||||
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
|
||||
|
||||
|_.Library|_.Min.Ver.|_.When needed|
|
||||
|Boost|1.35 or higher||
|
||||
|Boost|1.35||
|
||||
|GMP|4.2.2||
|
||||
|MPFR|2.4.0||
|
||||
|gettext|0.17|_optional_|
|
||||
|libedit|20090111-3.0|_optional_|
|
||||
|Python|2.4 or higher|_optional_|
|
||||
|Python|2.4|_optional_|
|
||||
|cppunit|1.12.1|_optional_, for @make check@|
|
||||
|doxygen|1.5.7.1|_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@|
|
||||
|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|
|
||||
|GMP|4.2.2||
|
||||
|
|
@ -74,12 +81,16 @@ h3. For building the beta or release branches
|
|||
|
||||
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>
|
||||
sudo port install boost +python25+st gmp mpfr gettext libedit \
|
||||
cppunit texlive doxygen graphviz texinfo lcov \
|
||||
sloccount pcre libofx expat
|
||||
sudo port install -f automake autoconf libtool python26
|
||||
libiconv +universal zlib +universal gmp +universal
|
||||
mpfr +universal ncurses +universal ncursesw +universal
|
||||
gettext +universal libedit +universal boost-jam
|
||||
boost +st+python26+icu cppunit texlive doxygen graphviz
|
||||
texinfo lcov sloccount
|
||||
</pre>
|
||||
|
||||
You can even just install the current Ledger *RELEASE* directly:
|
||||
|
|
@ -90,25 +101,38 @@ sudo port install ledger
|
|||
|
||||
h3. Ubuntu
|
||||
|
||||
If you're going to be build on Ubuntu, @sudo apt-get install ...@
|
||||
the following packages (current as of Ubuntu Hardy):
|
||||
If you're going to be build on Ubuntu, @sudo apt-get install ...@ the
|
||||
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>
|
||||
sudo apt-get install build-essential libtool autoconf automake \
|
||||
texinfo python-dev zlib1g-dev libbz2-dev stow libgmp3-dev \
|
||||
bjam libboost1.35-dev libboost-regex1.35-dev \
|
||||
libboost-date-time1.35-dev libboost-filesystem1.35-dev
|
||||
texinfo python-dev zlib1g-dev libbz2-dev libgmp3-dev \
|
||||
bjam gettext cvs libboost1.40-dev libboost-regex1.40-dev \
|
||||
libboost-date-time1.40-dev libboost-filesystem1.40-dev \
|
||||
libmpfr-dev
|
||||
</pre>
|
||||
|
||||
h2. Preparing the Build
|
||||
h2. Building
|
||||
|
||||
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
|
||||
you:
|
||||
@autogen.sh@, ./configure and make, I've prepared a script that does a lot more
|
||||
of the footwork for you:
|
||||
|
||||
<pre>
|
||||
./acprep pull # Make sure everything is pulled that needs to be
|
||||
./acprep
|
||||
./acprep update
|
||||
# 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>
|
||||
|
||||
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
|
||||
Ledger.
|
||||
|
||||
h2. Building
|
||||
|
||||
Once you have the dependencies installed and the source prepared for building,
|
||||
run @make check@ to get things started and confirm the result.
|
||||
You can run @make check@ to confirm the result, and @make install@ to install.
|
||||
|
||||
If you have extra CPU cycles to burn, try @./acprep proof@, which provides the
|
||||
most thorough shakedown of a healthy source tree.
|
||||
|
|
|
|||
111
acprep
111
acprep
|
|
@ -183,7 +183,7 @@ class PrepareBuild(CommandLineApp):
|
|||
self.current_flavor = 'debug'
|
||||
self.products_dir = None
|
||||
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_library_dirs = []
|
||||
|
||||
|
|
@ -551,14 +551,27 @@ class PrepareBuild(CommandLineApp):
|
|||
self.log.info('Looks like you are using MacPorts on OS X')
|
||||
packages = [
|
||||
'sudo', 'port', 'install', '-f',
|
||||
'automake', 'autoconf', 'libtool', 'python26',
|
||||
'libiconv', '+universal', 'zlib', '+universal',
|
||||
'gmp' ,'+universal', 'mpfr', '+universal',
|
||||
'ncurses', '+universal', 'ncursesw', '+universal',
|
||||
'gettext' ,'+universal', 'libedit' ,'+universal',
|
||||
'boost-jam', 'boost', '+st+python26+icu', 'cppunit',
|
||||
'texlive', 'doxygen', 'graphviz', 'texinfo',
|
||||
'lcov', 'sloccount'
|
||||
'automake',
|
||||
'autoconf',
|
||||
'libtool',
|
||||
'python26',
|
||||
'libiconv', '+universal',
|
||||
'zlib', '+universal',
|
||||
'gmp' ,'+universal',
|
||||
'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.execute(*packages)
|
||||
|
|
@ -570,23 +583,69 @@ class PrepareBuild(CommandLineApp):
|
|||
if exists('/etc/issue'):
|
||||
issue = open('/etc/issue')
|
||||
if issue.readline().startswith('Ubuntu'):
|
||||
self.log.info('Looks like you are using APT on Ubuntu')
|
||||
packages = [
|
||||
'sudo', 'apt-get', 'install',
|
||||
'build-essential',
|
||||
'libtool', 'autoconf', 'automake',
|
||||
'zlib1g-dev', 'libbz2-dev', 'python-dev',
|
||||
'libboost1.35-dev',
|
||||
'libboost-python1.35-dev',
|
||||
'libboost-regex1.35-dev',
|
||||
'libboost-date-time1.35-dev',
|
||||
'libboost-filesystem1.35-dev'
|
||||
'libgmp3-dev', 'libmpfr-dev', 'gettext',
|
||||
'libedit-dev', 'libcppunit-dev',
|
||||
#'texlive-full',
|
||||
#'doxygen', 'graphviz', 'texinfo',
|
||||
'lcov', 'sloccount'
|
||||
]
|
||||
release = open('/etc/lsb-release')
|
||||
info = release.read()
|
||||
release.close()
|
||||
if re.search('karmic', info):
|
||||
self.log.info('Looks like you are using APT on Ubuntu Karmic')
|
||||
packages = [
|
||||
'sudo', 'apt-get', 'install',
|
||||
'build-essential',
|
||||
'libtool',
|
||||
'autoconf',
|
||||
'automake',
|
||||
'zlib1g-dev',
|
||||
'libbz2-dev',
|
||||
'python-dev',
|
||||
'libgmp3-dev',
|
||||
'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.execute(*packages)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,38 +1,44 @@
|
|||
; -*- ledger -*-
|
||||
|
||||
N $
|
||||
|
||||
= account =~ /^Expenses:Books/
|
||||
(Liabilities:Taxes) -0.10
|
||||
= /^Expenses:Books/
|
||||
(Liabilities:Taxes) -0.10
|
||||
|
||||
~ Monthly
|
||||
Assets:Bank:Checking $500.00
|
||||
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
|
||||
Assets:Bank:Checking $1,000.00
|
||||
Equity:Opening Balances
|
||||
|
||||
2004/05/03=2004/05/01 * Investment balance
|
||||
Assets:Brokerage 50 AAPL @ $30.00
|
||||
Assets:Brokerage 50 AAPL @ $30.00
|
||||
Equity:Opening Balances
|
||||
|
||||
2004/05/14 * Páy dày
|
||||
Assets:Bank:Checking 500.00€
|
||||
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
|
||||
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
|
||||
Русский язык:Активы:Русский язык:Русский язык $1000.00
|
||||
Income:Salary
|
||||
|
||||
tag foo
|
||||
|
||||
2004/05/27 Book Store
|
||||
Expenses:Books $20.00
|
||||
Expenses:Cards $40.00
|
||||
Expenses:Docs $30.00
|
||||
Expenses:Books $20.00
|
||||
Expenses:Cards $40.00
|
||||
Expenses:Docs $30.00
|
||||
Liabilities:MasterCard
|
||||
|
||||
end tag
|
||||
|
|
@ -40,7 +46,7 @@ end tag
|
|||
2004/05/27 (100) Credit card company
|
||||
; This is an xact note!
|
||||
; Sample: Value
|
||||
Liabilities:MasterCard $20.00
|
||||
Liabilities:MasterCard $20.00
|
||||
; This is a posting note!
|
||||
; Sample: Another Value
|
||||
; :MyTag:
|
||||
|
|
|
|||
293
python/demo.py
Executable file
293
python/demo.py
Executable 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.'
|
||||
|
|
@ -106,8 +106,6 @@ private:
|
|||
#endif // HAVE_BOOST_SERIALIZATION
|
||||
};
|
||||
|
||||
shared_ptr<commodity_pool_t> amount_t::current_pool;
|
||||
|
||||
bool amount_t::is_initialized = false;
|
||||
|
||||
namespace {
|
||||
|
|
@ -203,7 +201,7 @@ namespace {
|
|||
}
|
||||
}
|
||||
|
||||
void amount_t::initialize(shared_ptr<commodity_pool_t> pool)
|
||||
void amount_t::initialize()
|
||||
{
|
||||
if (! is_initialized) {
|
||||
mpz_init(temp);
|
||||
|
|
@ -211,26 +209,35 @@ void amount_t::initialize(shared_ptr<commodity_pool_t> pool)
|
|||
mpfr_init(tempf);
|
||||
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;
|
||||
}
|
||||
current_pool = pool;
|
||||
}
|
||||
|
||||
void amount_t::initialize()
|
||||
{
|
||||
initialize(shared_ptr<commodity_pool_t>(new commodity_pool_t));
|
||||
}
|
||||
|
||||
void amount_t::shutdown()
|
||||
{
|
||||
current_pool.reset();
|
||||
|
||||
if (is_initialized) {
|
||||
mpz_clear(temp);
|
||||
mpq_clear(tempq);
|
||||
mpfr_clear(tempf);
|
||||
mpfr_clear(tempfb);
|
||||
|
||||
commodity_pool_t::current_pool.reset();
|
||||
|
||||
is_initialized = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -670,7 +677,7 @@ amount_t::value(const bool primary_only,
|
|||
if (in_terms_of && commodity() == *in_terms_of) {
|
||||
return *this;
|
||||
}
|
||||
else if (is_annotated() && annotation().price &&
|
||||
else if (has_annotation() && annotation().price &&
|
||||
annotation().has_flags(ANNOTATION_PRICE_FIXATED)) {
|
||||
return (*annotation().price * number()).rounded();
|
||||
}
|
||||
|
|
@ -696,7 +703,7 @@ amount_t::value(const bool primary_only,
|
|||
|
||||
amount_t amount_t::price() const
|
||||
{
|
||||
if (is_annotated() && annotation().price) {
|
||||
if (has_annotation() && annotation().price) {
|
||||
amount_t temp(*annotation().price);
|
||||
temp *= *this;
|
||||
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
|
||||
{
|
||||
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
|
||||
|
|
@ -794,7 +802,7 @@ void amount_t::annotate(const annotation_t& details)
|
|||
else if (! has_commodity())
|
||||
return; // ignore attempt to annotate a "bare commodity
|
||||
|
||||
if (commodity().is_annotated()) {
|
||||
if (commodity().has_annotation()) {
|
||||
this_ann = &as_annotated_commodity(commodity());
|
||||
this_base = &this_ann->referent();
|
||||
} else {
|
||||
|
|
@ -816,15 +824,15 @@ void amount_t::annotate(const annotation_t& details)
|
|||
DEBUG("amounts.commodities", "Annotated amount is " << *this);
|
||||
}
|
||||
|
||||
bool amount_t::is_annotated() const
|
||||
bool amount_t::has_annotation() const
|
||||
{
|
||||
if (! quantity)
|
||||
throw_(amount_error,
|
||||
_("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);
|
||||
return has_commodity() && commodity().is_annotated();
|
||||
return has_commodity() && commodity().has_annotation();
|
||||
}
|
||||
|
||||
annotation_t& amount_t::annotation()
|
||||
|
|
@ -833,7 +841,7 @@ annotation_t& amount_t::annotation()
|
|||
throw_(amount_error,
|
||||
_("Cannot return commodity annotation details of an uninitialized amount"));
|
||||
|
||||
if (! commodity().is_annotated())
|
||||
if (! commodity().has_annotation())
|
||||
throw_(amount_error,
|
||||
_("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()) {
|
||||
commodity_ = NULL;
|
||||
} else {
|
||||
commodity_ = current_pool->find(symbol);
|
||||
commodity_ = commodity_pool_t::current_pool->find(symbol);
|
||||
if (! commodity_) {
|
||||
commodity_ = current_pool->create(symbol);
|
||||
commodity_ = commodity_pool_t::current_pool->create(symbol);
|
||||
newly_created = true;
|
||||
}
|
||||
assert(commodity_);
|
||||
|
||||
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
|
||||
|
|
@ -1206,7 +1215,6 @@ void to_xml(std::ostream& out, const amount_t& amt, bool commodity_details)
|
|||
template<class Archive>
|
||||
void amount_t::serialize(Archive& ar, const unsigned int /* version */)
|
||||
{
|
||||
ar & current_pool;
|
||||
ar & is_initialized;
|
||||
ar & quantity;
|
||||
ar & commodity_;
|
||||
|
|
|
|||
|
|
@ -97,12 +97,8 @@ class amount_t
|
|||
ordered_field_operators<amount_t, long> > > >
|
||||
{
|
||||
public:
|
||||
/** Indicates which commodity pool should be used. */
|
||||
static shared_ptr<commodity_pool_t> current_pool;
|
||||
|
||||
/** Ready the amount subsystem for use.
|
||||
@note Normally called by session_t::initialize(). */
|
||||
static void initialize(shared_ptr<commodity_pool_t> pool);
|
||||
static void initialize();
|
||||
/** Shutdown the amount subsystem and free all resources.
|
||||
@note Normally called by session_t::shutdown(). */
|
||||
|
|
@ -577,7 +573,7 @@ public:
|
|||
been stripped.
|
||||
*/
|
||||
void annotate(const annotation_t& details);
|
||||
bool is_annotated() const;
|
||||
bool has_annotation() const;
|
||||
|
||||
annotation_t& annotation();
|
||||
const annotation_t& annotation() const {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
return (! comm.is_annotated() ||
|
||||
return (! comm.has_annotation() ||
|
||||
(keep_price && keep_date && keep_tag && ! only_actuals));
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -43,21 +43,21 @@ balance_t::balance_t(const double val)
|
|||
{
|
||||
TRACE_CTOR(balance_t, "const double");
|
||||
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)
|
||||
{
|
||||
TRACE_CTOR(balance_t, "const unsigned long");
|
||||
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)
|
||||
{
|
||||
TRACE_CTOR(balance_t, "const long");
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -600,11 +600,11 @@ bool compare_amount_commodities::operator()(const amount_t * left,
|
|||
if (cmp != 0)
|
||||
return cmp < 0;
|
||||
|
||||
if (! leftcomm.is_annotated()) {
|
||||
return rightcomm.is_annotated();
|
||||
if (! leftcomm.has_annotation()) {
|
||||
return rightcomm.has_annotation();
|
||||
}
|
||||
else if (! rightcomm.is_annotated()) {
|
||||
return ! leftcomm.is_annotated();
|
||||
else if (! rightcomm.has_annotation()) {
|
||||
return ! leftcomm.has_annotation();
|
||||
}
|
||||
else {
|
||||
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 (comm.is_annotated())
|
||||
if (comm.has_annotation())
|
||||
to_xml(out, as_annotated_commodity(comm).details);
|
||||
|
||||
if (comm.varied_history()) {
|
||||
|
|
|
|||
|
|
@ -251,7 +251,7 @@ public:
|
|||
return *this;
|
||||
}
|
||||
|
||||
bool is_annotated() const {
|
||||
bool has_annotation() const {
|
||||
return annotated;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -77,7 +77,6 @@ journal_t::~journal_t()
|
|||
checked_delete(xact);
|
||||
|
||||
checked_delete(master);
|
||||
commodity_pool.reset();
|
||||
}
|
||||
|
||||
void journal_t::initialize()
|
||||
|
|
@ -85,21 +84,6 @@ void journal_t::initialize()
|
|||
master = new account_t;
|
||||
bucket = NULL;
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -47,7 +47,6 @@
|
|||
|
||||
namespace ledger {
|
||||
|
||||
class commodity_pool_t;
|
||||
class xact_base_t;
|
||||
class xact_t;
|
||||
class auto_xact_t;
|
||||
|
|
@ -112,8 +111,6 @@ public:
|
|||
std::list<fileinfo_t> sources;
|
||||
bool was_loaded;
|
||||
|
||||
shared_ptr<commodity_pool_t> commodity_pool;
|
||||
|
||||
journal_t();
|
||||
journal_t(const path& pathname);
|
||||
journal_t(const string& str);
|
||||
|
|
|
|||
|
|
@ -624,7 +624,7 @@ bool expr_t::op_t::print(std::ostream& out, const context_t& context) const
|
|||
}
|
||||
|
||||
if (! symbol.empty()) {
|
||||
if (amount_t::current_pool->find(symbol))
|
||||
if (commodity_pool_t::current_pool->find(symbol))
|
||||
out << '@';
|
||||
out << symbol;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,8 @@
|
|||
|
||||
namespace ledger {
|
||||
|
||||
shared_ptr<commodity_pool_t> commodity_pool_t::current_pool;
|
||||
|
||||
commodity_pool_t::commodity_pool_t()
|
||||
: default_commodity(NULL), keep_base(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());
|
||||
|
||||
DEBUG("commodity.download", "Looking up symbol: " << symbol);
|
||||
if (commodity_t * commodity =
|
||||
amount_t::current_pool->find_or_create(symbol)) {
|
||||
if (commodity_t * commodity = find_or_create(symbol)) {
|
||||
DEBUG("commodity.download", "Adding price for " << symbol << ": "
|
||||
<< point.when << " " << point.price);
|
||||
commodity->add_price(point.when, point.price, true);
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ struct cost_breakdown_t
|
|||
|
||||
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
|
||||
|
|
@ -65,7 +66,6 @@ class commodity_pool_t : public noncopyable
|
|||
*/
|
||||
typedef std::map<string, commodity_t *> commodities_map;
|
||||
|
||||
public:
|
||||
commodities_map commodities;
|
||||
commodity_t * null_commodity;
|
||||
commodity_t * default_commodity;
|
||||
|
|
@ -76,6 +76,8 @@ public:
|
|||
long quote_leeway; // --leeway=
|
||||
bool get_quotes; // --download
|
||||
|
||||
static shared_ptr<commodity_pool_t> current_pool;
|
||||
|
||||
function<optional<price_point_t>
|
||||
(commodity_t& commodity, const optional<commodity_t&>& in_terms_of)>
|
||||
get_commodity_quote;
|
||||
|
|
@ -136,6 +138,7 @@ private:
|
|||
|
||||
template<class Archive>
|
||||
void serialize(Archive& ar, const unsigned int /* version */) {
|
||||
ar & current_pool;
|
||||
ar & commodities;
|
||||
ar & null_commodity;
|
||||
ar & default_commodity;
|
||||
|
|
|
|||
|
|
@ -45,26 +45,16 @@ using namespace boost::python;
|
|||
namespace {
|
||||
|
||||
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,
|
||||
const bool primary_only) {
|
||||
return amount.value(primary_only);
|
||||
commodity_t& in_terms_of) {
|
||||
return amount.value(false, CURRENT_TIME(), in_terms_of);
|
||||
}
|
||||
|
||||
boost::optional<amount_t>
|
||||
py_value_2(const amount_t& amount,
|
||||
const bool primary_only,
|
||||
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);
|
||||
boost::optional<amount_t> py_value_2(const amount_t& amount,
|
||||
commodity_t& in_terms_of,
|
||||
datetime_t& moment) {
|
||||
return amount.value(false, moment, in_terms_of);
|
||||
}
|
||||
|
||||
void py_parse_2(amount_t& amount, object in, unsigned char flags) {
|
||||
|
|
@ -97,8 +87,15 @@ namespace {
|
|||
}
|
||||
}
|
||||
|
||||
void py_amount_initialize() {
|
||||
amount_t::initialize();
|
||||
annotation_t& py_amount_annotation(amount_t& amount) {
|
||||
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
|
||||
|
|
@ -113,11 +110,7 @@ EXC_TRANSLATOR(amount_error)
|
|||
void export_amount()
|
||||
{
|
||||
class_< amount_t > ("Amount")
|
||||
.add_static_property("current_pool",
|
||||
make_getter(&amount_t::current_pool,
|
||||
return_internal_reference<>()))
|
||||
|
||||
.def("initialize", py_amount_initialize) // only for the PyUnitTests
|
||||
.def("initialize", &amount_t::initialize) // only for the PyUnitTests
|
||||
.staticmethod("initialize")
|
||||
.def("shutdown", &amount_t::shutdown)
|
||||
.staticmethod("shutdown")
|
||||
|
|
@ -196,11 +189,11 @@ internal precision."))
|
|||
.def(self / long())
|
||||
.def(long() / self)
|
||||
|
||||
.def("precision", &amount_t::precision)
|
||||
.def("keep_precision", &amount_t::keep_precision)
|
||||
.def("set_keep_precision", &amount_t::set_keep_precision, args("keep"),
|
||||
_("Set whether an amount should always display at full precision."))
|
||||
.def("display_precision", &amount_t::display_precision)
|
||||
.add_property("precision", &amount_t::precision)
|
||||
.add_property("display_precision", &amount_t::display_precision)
|
||||
.add_property("keep_precision",
|
||||
&amount_t::keep_precision,
|
||||
&amount_t::set_keep_precision)
|
||||
|
||||
.def("negated", &amount_t::negated)
|
||||
.def("in_place_negate", &amount_t::in_place_negate,
|
||||
|
|
@ -237,9 +230,8 @@ internal precision."))
|
|||
return_internal_reference<>())
|
||||
|
||||
.def("value", py_value_0)
|
||||
.def("value", py_value_1, args("primary_only"))
|
||||
.def("value", py_value_2, args("primary_only", "moment"))
|
||||
.def("value", py_value_3, args("primary_only", "moment", "in_terms_of"))
|
||||
.def("value", py_value_1, args("in_terms_of"))
|
||||
.def("value", py_value_2, args("in_terms_of", "moment"))
|
||||
|
||||
.def("price", &amount_t::price)
|
||||
|
||||
|
|
@ -262,21 +254,23 @@ internal precision."))
|
|||
.def("__repr__", &amount_t::to_fullstring)
|
||||
.def("quantity_string", &amount_t::quantity_string)
|
||||
|
||||
.def("commodity", &amount_t::commodity,
|
||||
return_internal_reference<>())
|
||||
.add_property("commodity",
|
||||
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("set_commodity", &amount_t::set_commodity,
|
||||
with_custodian_and_ward<1, 2>())
|
||||
.def("clear_commodity", &amount_t::clear_commodity)
|
||||
|
||||
.def("number", &amount_t::number)
|
||||
|
||||
.def("annotate", &amount_t::annotate)
|
||||
.def("is_annotated", &amount_t::is_annotated)
|
||||
#if 0
|
||||
.def("annotation", &amount_t::annotation)
|
||||
#endif
|
||||
.def("strip_annotations", &amount_t::strip_annotations)
|
||||
.def("has_annotation", &amount_t::has_annotation)
|
||||
.add_property("annotation",
|
||||
make_function(py_amount_annotation,
|
||||
return_internal_reference<>()))
|
||||
.def("strip_annotations", py_strip_annotations_0)
|
||||
.def("strip_annotations", py_strip_annotations_1)
|
||||
|
||||
.def("parse", py_parse_1)
|
||||
.def("parse", py_parse_2)
|
||||
|
|
|
|||
|
|
@ -45,28 +45,18 @@ using namespace boost::python;
|
|||
namespace {
|
||||
|
||||
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,
|
||||
const bool primary_only) {
|
||||
return balance.value(primary_only);
|
||||
commodity_t& in_terms_of) {
|
||||
return balance.value(false, CURRENT_TIME(), in_terms_of);
|
||||
}
|
||||
|
||||
boost::optional<balance_t>
|
||||
py_value_2(const balance_t& balance,
|
||||
const bool primary_only,
|
||||
const boost::optional<datetime_t>& moment) {
|
||||
return balance.value(primary_only, moment);
|
||||
boost::optional<balance_t> py_value_2(const balance_t& balance,
|
||||
commodity_t& in_terms_of,
|
||||
datetime_t& moment) {
|
||||
return balance.value(false, moment, in_terms_of);
|
||||
}
|
||||
|
||||
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>
|
||||
py_commodity_amount_0(const balance_t& balance) {
|
||||
return balance.commodity_amount();
|
||||
|
|
@ -108,6 +98,13 @@ namespace {
|
|||
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
|
||||
|
||||
#define EXC_TRANSLATOR(type) \
|
||||
|
|
@ -193,9 +190,8 @@ void export_balance()
|
|||
return_internal_reference<>())
|
||||
|
||||
.def("value", py_value_0)
|
||||
.def("value", py_value_1, args("primary_only"))
|
||||
.def("value", py_value_2, args("primary_only", "moment"))
|
||||
.def("value", py_value_3, args("primary_only", "moment", "in_terms_of"))
|
||||
.def("value", py_value_1, args("in_terms_of"))
|
||||
.def("value", py_value_2, args("in_terms_of", "moment"))
|
||||
|
||||
.def("price", &balance_t::price)
|
||||
|
||||
|
|
@ -215,7 +211,8 @@ void export_balance()
|
|||
|
||||
.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)
|
||||
;
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
#include <system.hh>
|
||||
|
||||
#include "pyinterp.h"
|
||||
#include "pyutils.h"
|
||||
#include "commodity.h"
|
||||
#include "annotate.h"
|
||||
#include "pool.h"
|
||||
|
|
@ -81,6 +82,12 @@ namespace {
|
|||
|
||||
// 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,
|
||||
commodity_t& commodity,
|
||||
const amount_t& per_unit_cost,
|
||||
|
|
@ -99,6 +106,77 @@ namespace {
|
|||
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,
|
||||
const datetime_t& date, const amount_t& price) {
|
||||
commodity.add_price(date, price);
|
||||
|
|
@ -123,16 +201,46 @@ namespace {
|
|||
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
|
||||
|
||||
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",
|
||||
make_getter(&commodity_pool_t::null_commodity,
|
||||
return_internal_reference<>()),
|
||||
make_setter(&commodity_pool_t::null_commodity,
|
||||
with_custodian_and_ward<1, 2>()))
|
||||
return_internal_reference<>()))
|
||||
.add_property("default_commodity",
|
||||
make_getter(&commodity_pool_t::default_commodity,
|
||||
return_internal_reference<>()),
|
||||
|
|
@ -157,25 +265,46 @@ void export_commodity()
|
|||
|
||||
.def("make_qualified_name", &commodity_pool_t::make_qualified_name)
|
||||
|
||||
.def("create", py_create_1, return_internal_reference<>())
|
||||
.def("create", py_create_2, return_internal_reference<>())
|
||||
.def("create", py_create_1,
|
||||
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,
|
||||
return_internal_reference<>())
|
||||
return_value_policy<reference_existing_object>())
|
||||
.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_2, return_internal_reference<>())
|
||||
.def("find", py_find_1, return_value_policy<reference_existing_object>())
|
||||
.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_5)
|
||||
|
||||
.def("parse_price_directive", &commodity_pool_t::parse_price_directive)
|
||||
.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_SUFFIXED") = COMMODITY_STYLE_SUFFIXED;
|
||||
scope().attr("COMMODITY_STYLE_SEPARATED") = COMMODITY_STYLE_SEPARATED;
|
||||
|
|
@ -209,33 +338,30 @@ void export_commodity()
|
|||
.def("symbol_needs_quotes", &commodity_t::symbol_needs_quotes)
|
||||
.staticmethod("symbol_needs_quotes")
|
||||
|
||||
#if 0
|
||||
.def("referent", &commodity_t::referent,
|
||||
return_internal_reference<>())
|
||||
#endif
|
||||
.add_property("referent",
|
||||
make_function(py_commodity_referent,
|
||||
return_value_policy<reference_existing_object>()))
|
||||
|
||||
.def("is_annotated", &commodity_t::is_annotated)
|
||||
.def("strip_annotations", &commodity_t::strip_annotations,
|
||||
return_internal_reference<>())
|
||||
.def("has_annotation", &commodity_t::has_annotation)
|
||||
.def("strip_annotations", py_strip_annotations_0,
|
||||
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("pool", &commodity_t::pool,
|
||||
return_internal_reference<>())
|
||||
return_value_policy<reference_existing_object>())
|
||||
|
||||
.def("base_symbol", &commodity_t::base_symbol)
|
||||
.def("symbol", &commodity_t::symbol)
|
||||
.def("mapping_key", &commodity_t::mapping_key)
|
||||
.add_property("base_symbol", &commodity_t::base_symbol)
|
||||
.add_property("symbol", &commodity_t::symbol)
|
||||
.add_property("mapping_key", &commodity_t::mapping_key)
|
||||
|
||||
.def("name", &commodity_t::name)
|
||||
.def("set_name", &commodity_t::set_name)
|
||||
.def("note", &commodity_t::note)
|
||||
.def("set_note", &commodity_t::set_note)
|
||||
.def("precision", &commodity_t::precision)
|
||||
.def("set_precision", &commodity_t::set_precision)
|
||||
.def("smaller", &commodity_t::smaller)
|
||||
.def("set_smaller", &commodity_t::set_smaller)
|
||||
.def("larger", &commodity_t::larger)
|
||||
.def("set_larger", &commodity_t::set_larger)
|
||||
.add_property("name", &commodity_t::name, &commodity_t::set_name)
|
||||
.add_property("note", &commodity_t::note, &commodity_t::set_note)
|
||||
.add_property("precision", &commodity_t::precision,
|
||||
&commodity_t::set_precision)
|
||||
.add_property("smaller", &commodity_t::smaller, &commodity_t::set_smaller)
|
||||
.add_property("larger", &commodity_t::larger, &commodity_t::set_larger)
|
||||
|
||||
.def("add_price", py_add_price_2)
|
||||
.def("add_price", py_add_price_3)
|
||||
|
|
@ -257,9 +383,7 @@ void export_commodity()
|
|||
.def("drop_flags", &supports_flags<>::drop_flags)
|
||||
#endif
|
||||
|
||||
.add_property("price",
|
||||
make_getter(&annotation_t::price),
|
||||
make_setter(&annotation_t::price))
|
||||
.add_property("price", py_price, py_set_price)
|
||||
.add_property("date",
|
||||
make_getter(&annotation_t::date),
|
||||
make_setter(&annotation_t::date))
|
||||
|
|
@ -306,13 +430,14 @@ void export_commodity()
|
|||
.def(self == self)
|
||||
.def(self == other<commodity_t>())
|
||||
|
||||
#if 0
|
||||
.def("referent", &annotated_commodity_t::referent,
|
||||
return_internal_reference<>())
|
||||
#endif
|
||||
.add_property("referent",
|
||||
make_function(py_annotated_commodity_referent,
|
||||
return_value_policy<reference_existing_object>()))
|
||||
|
||||
.def("strip_annotations", &annotated_commodity_t::strip_annotations,
|
||||
return_internal_reference<>())
|
||||
.def("strip_annotations", py_strip_ann_annotations_0,
|
||||
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)
|
||||
;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,10 +52,6 @@ namespace {
|
|||
return *journal.master;
|
||||
}
|
||||
|
||||
commodity_pool_t& py_commodity_pool(journal_t& journal) {
|
||||
return *journal.commodity_pool;
|
||||
}
|
||||
|
||||
long xacts_len(journal_t& journal)
|
||||
{
|
||||
return journal.xacts.size();
|
||||
|
|
@ -276,10 +272,6 @@ void export_journal()
|
|||
with_custodian_and_ward_postcall<1, 0> >()),
|
||||
make_setter(&journal_t::bucket))
|
||||
.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("remove_account", &journal_t::remove_account)
|
||||
|
|
|
|||
|
|
@ -47,7 +47,21 @@ BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(set_string_overloads, set_string, 0, 2)
|
|||
|
||||
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()) {
|
||||
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) {
|
||||
std::ostringstream buf;
|
||||
value.dump(buf);
|
||||
|
|
@ -85,10 +89,20 @@ namespace {
|
|||
return buf.str();
|
||||
}
|
||||
|
||||
void py_set_string(value_t& amount, const string& str) {
|
||||
return amount.set_string(str);
|
||||
void py_set_string(value_t& value, const 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
|
||||
|
||||
#define EXC_TRANSLATOR(type) \
|
||||
|
|
@ -243,6 +257,10 @@ void export_value()
|
|||
.def("unreduced", &value_t::unreduced)
|
||||
.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("price", &value_t::price)
|
||||
.def("exchange_commodities", &value_t::exchange_commodities,
|
||||
|
|
@ -306,16 +324,16 @@ void export_value()
|
|||
.def("number", &value_t::number)
|
||||
|
||||
.def("annotate", &value_t::annotate)
|
||||
.def("is_annotated", &value_t::is_annotated)
|
||||
#if 0
|
||||
.def("annotation", &value_t::annotation)
|
||||
#endif
|
||||
.def("strip_annotations", &value_t::strip_annotations)
|
||||
.def("has_annotation", &value_t::has_annotation)
|
||||
.add_property("annotation",
|
||||
make_function(py_value_annotation,
|
||||
return_internal_reference<>()))
|
||||
.def("strip_annotations", py_strip_annotations_0)
|
||||
.def("strip_annotations", py_strip_annotations_1)
|
||||
|
||||
#if 0
|
||||
.def("__getitem__", &value_t::operator[])
|
||||
#endif
|
||||
.def("__getattr__", py_value_getattr)
|
||||
.def("push_back", &value_t::push_back)
|
||||
.def("pop_back", &value_t::pop_back)
|
||||
.def("size", &value_t::size)
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
||||
// Use expr to create the PyObject corresponding to x
|
||||
|
|
|
|||
|
|
@ -76,13 +76,13 @@ commodity_quote_from_script(commodity_t& commodity,
|
|||
DEBUG("commodity.download", "downloaded quote: " << buf);
|
||||
|
||||
if (optional<price_point_t> point =
|
||||
amount_t::current_pool->parse_price_directive(buf)) {
|
||||
if (amount_t::current_pool->price_db) {
|
||||
commodity_pool_t::current_pool->parse_price_directive(buf)) {
|
||||
if (commodity_pool_t::current_pool->price_db) {
|
||||
#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);
|
||||
#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);
|
||||
#endif
|
||||
database << "P "
|
||||
|
|
|
|||
|
|
@ -69,18 +69,17 @@ void report_t::normalize_options(const string& verb)
|
|||
item_t::use_effective_date = (HANDLED(effective) &&
|
||||
! HANDLED(actual_dates));
|
||||
|
||||
session.journal->commodity_pool->keep_base = HANDLED(base);
|
||||
session.journal->commodity_pool->get_quotes = session.HANDLED(download);
|
||||
commodity_pool_t::current_pool->keep_base = HANDLED(base);
|
||||
commodity_pool_t::current_pool->get_quotes = session.HANDLED(download);
|
||||
|
||||
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();
|
||||
|
||||
if (session.HANDLED(price_db_))
|
||||
session.journal->commodity_pool->price_db =
|
||||
session.HANDLER(price_db_).str();
|
||||
commodity_pool_t::current_pool->price_db = session.HANDLER(price_db_).str();
|
||||
else
|
||||
session.journal->commodity_pool->price_db = none;
|
||||
commodity_pool_t::current_pool->price_db = none;
|
||||
|
||||
if (HANDLED(date_format_))
|
||||
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)
|
||||
{
|
||||
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());
|
||||
if (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)
|
||||
{
|
||||
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());
|
||||
if (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)
|
||||
{
|
||||
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());
|
||||
if (details.tag)
|
||||
return string_value(*details.tag);
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ void set_session_context(session_t * session)
|
|||
{
|
||||
if (session) {
|
||||
times_initialize();
|
||||
amount_t::initialize(session->journal->commodity_pool);
|
||||
amount_t::initialize();
|
||||
|
||||
amount_t::parse_conversion("1.0m", "60s");
|
||||
amount_t::parse_conversion("1.0h", "60m");
|
||||
|
|
@ -179,7 +179,7 @@ void session_t::close_journal_files()
|
|||
amount_t::shutdown();
|
||||
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -490,7 +490,7 @@ void instance_t::default_commodity_directive(char * line)
|
|||
{
|
||||
amount_t amt(skip_ws(line + 1));
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
@ -511,7 +511,7 @@ void instance_t::price_conversion_directive(char * line)
|
|||
void instance_t::price_xact_directive(char * line)
|
||||
{
|
||||
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)
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1321,10 +1321,13 @@ date_parser_t::lexer_t::token_t date_parser_t::lexer_t::next_token()
|
|||
catch (...) {}
|
||||
}
|
||||
|
||||
start = begin;
|
||||
|
||||
string term;
|
||||
bool alnum = std::isalnum(*begin);
|
||||
for (start = begin; (begin != end && ! std::isspace(*begin) &&
|
||||
alnum == std::isalnum(*begin)); begin++)
|
||||
for (; (begin != end && ! std::isspace(*begin) &&
|
||||
((alnum && static_cast<bool>(std::isalnum(*begin))) ||
|
||||
(! alnum && ! static_cast<bool>(std::isalnum(*begin))))); begin++)
|
||||
term.push_back(*begin);
|
||||
|
||||
if (! term.empty()) {
|
||||
|
|
|
|||
|
|
@ -1353,7 +1353,8 @@ value_t value_t::exchange_commodities(const std::string& commodities,
|
|||
p;
|
||||
p = std::strtok(NULL, ",")) {
|
||||
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);
|
||||
if (! result.is_null())
|
||||
return result;
|
||||
|
|
@ -1523,10 +1524,10 @@ void value_t::annotate(const annotation_t& details)
|
|||
throw_(value_error, _("Cannot annotate %1") << label());
|
||||
}
|
||||
|
||||
bool value_t::is_annotated() const
|
||||
bool value_t::has_annotation() const
|
||||
{
|
||||
if (is_amount())
|
||||
return as_amount().is_annotated();
|
||||
return as_amount().has_annotation();
|
||||
else
|
||||
throw_(value_error,
|
||||
_("Cannot determine whether %1 is annotated") << label());
|
||||
|
|
|
|||
|
|
@ -774,7 +774,7 @@ public:
|
|||
* Annotated commodity methods.
|
||||
*/
|
||||
void annotate(const annotation_t& details);
|
||||
bool is_annotated() const;
|
||||
bool has_annotation() const;
|
||||
|
||||
annotation_t& annotation();
|
||||
const annotation_t& annotation() const {
|
||||
|
|
|
|||
13
src/xact.cc
13
src/xact.cc
|
|
@ -122,7 +122,7 @@ bool xact_base_t::finalize()
|
|||
amount_t& p(post->cost ? *post->cost : post->amount);
|
||||
if (! p.is_null()) {
|
||||
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) {
|
||||
// If the amount has no cost, but is annotated with a per-unit
|
||||
// price, use the price times the amount as the cost
|
||||
|
|
@ -221,7 +221,7 @@ bool xact_base_t::finalize()
|
|||
|
||||
foreach (post_t * post, posts) {
|
||||
if (! post->amount.is_null()) {
|
||||
if (post->amount.is_annotated())
|
||||
if (post->amount.has_annotation())
|
||||
top_post = post;
|
||||
else if (! top_post)
|
||||
top_post = post;
|
||||
|
|
@ -260,7 +260,7 @@ bool xact_base_t::finalize()
|
|||
foreach (post_t * post, posts) {
|
||||
if (post != top_post && post->must_balance() &&
|
||||
! post->amount.is_null() &&
|
||||
post->amount.is_annotated() &&
|
||||
post->amount.has_annotation() &&
|
||||
post->amount.annotation().price) {
|
||||
amount_t temp = *post->amount.annotation().price * post->amount;
|
||||
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"));
|
||||
|
||||
cost_breakdown_t breakdown =
|
||||
amount_t::current_pool->exchange(post->amount, *post->cost, false,
|
||||
datetime_t(date(), time_duration(0, 0, 0, 0)));
|
||||
commodity_pool_t::current_pool->exchange
|
||||
(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.final_cost.commodity()) {
|
||||
if (amount_t gain_loss = (breakdown.basis_cost -
|
||||
|
|
|
|||
91
test/baseline/test-sample.dat
Normal file
91
test/baseline/test-sample.dat
Normal 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
|
||||
|
|
@ -158,6 +158,8 @@ for line in fd.readlines():
|
|||
line = re.sub('false', 'False', line)
|
||||
line = re.sub('CURRENT_TIME\(\)', 'datetime.now()', 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]+)UL', '\\1L', line)
|
||||
line = re.sub(';', '', line)
|
||||
|
|
|
|||
|
|
@ -1,42 +1,52 @@
|
|||
; -*- ledger -*-
|
||||
|
||||
N $
|
||||
|
||||
= account =~ /^Expenses:Books/
|
||||
(Liabilities:Taxes) -0.10
|
||||
= /^Expenses:Books/
|
||||
(Liabilities:Taxes) -0.10
|
||||
|
||||
~ Monthly
|
||||
Assets:Bank:Checking $500.00
|
||||
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
|
||||
Assets:Bank:Checking $1,000.00
|
||||
Equity:Opening Balances
|
||||
|
||||
2004/05/03=2004/05/01 * Investment balance
|
||||
Assets:Brokerage 50 AAPL @ $30.00
|
||||
Assets:Brokerage 50 AAPL @ $30.00
|
||||
Equity:Opening Balances
|
||||
|
||||
2004/05/14 * Páy dày
|
||||
Assets:Bank:Checking 500.00€
|
||||
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
|
||||
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
|
||||
Русский язык:Активы:Русский язык:Русский язык $1000.00
|
||||
Income:Salary
|
||||
|
||||
tag foo
|
||||
|
||||
2004/05/27 Book Store
|
||||
Expenses:Books $20.00
|
||||
Expenses:Cards $40.00
|
||||
Expenses:Docs $30.00
|
||||
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
|
||||
Liabilities:MasterCard $20.00
|
||||
; This is a posting note!
|
||||
; Sample: Another Value
|
||||
; :MyTag:
|
||||
|
|
|
|||
|
|
@ -4,16 +4,16 @@
|
|||
>>>2
|
||||
While parsing file "$sourcepath/src/amount.h", line 67:
|
||||
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
|
||||
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_
|
||||
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_
|
||||
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_
|
||||
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&
|
||||
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&
|
||||
=== 7
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue