summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.dir-locals.el2
-rw-r--r--.travis.yml7
-rw-r--r--CMakeLists.txt4
-rw-r--r--INSTALL.md2
-rw-r--r--README.md27
-rwxr-xr-xacprep10
-rw-r--r--appveyor.yml2
-rw-r--r--cmake/FindUtfcpp.cmake3
-rw-r--r--contrib/ledger-completion.bash2
-rwxr-xr-xcontrib/non-profit-audit-reports/cash-receipts-and-disbursments-journals.plx2
-rw-r--r--default.nix5
-rw-r--r--doc/NEWS71
-rw-r--r--doc/ledger.13
-rw-r--r--doc/ledger3.texi189
-rw-r--r--src/amount.cc4
-rw-r--r--src/context.h1
-rw-r--r--src/error.h5
-rw-r--r--src/filters.cc25
-rw-r--r--src/global.h2
-rw-r--r--src/output.cc7
-rw-r--r--src/print.cc4
-rw-r--r--src/session.cc4
-rw-r--r--src/session.h4
-rw-r--r--src/system.hh.in4
-rw-r--r--src/textual.cc57
-rw-r--r--src/times.cc31
-rw-r--r--src/utils.h10
-rw-r--r--src/xact.cc10
-rw-r--r--test/python/JournalTest.py18
-rw-r--r--test/regress/1127.test15
-rw-r--r--test/regress/1187_5.test36
-rw-r--r--test/regress/1626.test28
-rw-r--r--test/regress/1703.test11
-rw-r--r--test/regress/7F3650FD.test2
-rw-r--r--test/regress/BBFA1759.test2
-rw-r--r--test/regress/fix-missing-trans-in-last-budget-period.test79
36 files changed, 496 insertions, 192 deletions
diff --git a/.dir-locals.el b/.dir-locals.el
index de699515..34761815 100644
--- a/.dir-locals.el
+++ b/.dir-locals.el
@@ -4,7 +4,7 @@
((nil
(tab-width . 2)
(sentence-end-double-space . t)
- (bug-reference-url-format . "http://bugs.ledger-cli.org/show_bug.cgi?id=%s"))
+ (bug-reference-url-format . "https://github.com/ledger/ledger/issues/%s"))
(c-mode
(c-file-style . "ledger")
(c-style-alist
diff --git a/.travis.yml b/.travis.yml
index 94489bdc..d10d4466 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -38,7 +38,7 @@ matrix:
# either crashes clang or results in a ledger binary that crashes with SIGSEGV.
- os: osx
compiler: gcc
- # On Mac OS X building ledger with GNU GCC 4.8 fails due to
+ # On macOS building ledger with GNU GCC 4.8 fails due to
# undefined symbols, maybe because boost was not being built with g++-4.8.
# Undefined symbols for architecture x86_64:
# "boost::re_detail::perl_matcher<char const*, std::allocator<boost::sub_match<char const*> >, boost::regex_traits<char, boost::cpp_regex_traits<char> > >::construct_init(boost::basic_regex<char, boost::regex_traits<char, boost::cpp_regex_traits<char> > > const&, boost::regex_constants::_match_flags)", referenced from:
@@ -49,7 +49,7 @@ matrix:
# bool boost::regex_search<char const*, char, boost::regex_traits<char, boost::cpp_regex_traits<char> > >(char const*, char const*, boost::basic_regex<char, boost::regex_traits<char, boost::cpp_regex_traits<char> > > const&, boost::regex_constants::_match_flags) in global.cc.o
- os: osx
compiler: clang
- # On Mac OS X building ledger with clang fails due to
+ # On macOS building ledger with clang fails due to
# dyld: Library not loaded: libboost_python.dylib
# Referenced from: /Users/travis/build/ledger/ledger/ledger
# Reason: image not found
@@ -83,7 +83,8 @@ addons:
#- libboost-serialization1.55-dev
before_install:
- - if [ "${TRAVIS_BRANCH}" = "master" ]; then export BOOST_VERSION="${BOOST_VERSION_MIN}"; else export BOOST_VERSION="${BOOST_VERSION_MAX}"; fi
+ # - if [ "${TRAVIS_BRANCH}" = "master" ]; then export BOOST_VERSION="${BOOST_VERSION_MIN}"; else export BOOST_VERSION="${BOOST_VERSION_MAX}"; fi
+ - export BOOST_VERSION="${BOOST_VERSION_MAX}"
- if [ -n "${BOOST_VERSION}" ]; then export BOOST_ROOT="${TRAVIS_BUILD_DIR}/../boost-trunk"; export CMAKE_MODULE_PATH="${BOOST_ROOT}"; fi
- if [ "${CXX}" = "g++" ]; then export CXX="$(which g++-4.8)"; export CC="$(which gcc-4.8)"; fi
- if [ "${TRAVIS_OS_NAME}" = "osx" ]; then export DYLD_LIBRARY_PATH="${BOOST_ROOT}/lib"; fi
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f8dbd892..c85a5157 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -96,8 +96,6 @@ include(CheckCXXSourceCompiles)
include(CheckCXXSourceRuns)
include(CMakePushCheckState)
-check_function_exists(access HAVE_ACCESS)
-check_function_exists(realpath HAVE_REALPATH)
check_function_exists(getpwuid HAVE_GETPWUID)
check_function_exists(getpwnam HAVE_GETPWNAM)
check_function_exists(ioctl HAVE_IOCTL)
@@ -260,7 +258,7 @@ macro(add_ledger_library_dependencies _target)
endif()
if (HAVE_BOOST_PYTHON)
if(CMAKE_SYSTEM_NAME STREQUAL Darwin)
- # Don't link directly to a Python framework on OS X, to avoid segfaults
+ # Don't link directly to a Python framework on macOS, to avoid segfaults
# when the module is imported from a different interpreter
target_link_libraries(${_target} ${Boost_LIBRARIES})
set_target_properties(${_target} PROPERTIES LINK_FLAGS "-undefined dynamic_lookup")
diff --git a/INSTALL.md b/INSTALL.md
index 23d0566b..a366dbfd 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -115,7 +115,7 @@ Q: Whenever I try to use the Python support, I get a segfault
A: Make sure that the boost_python library you linked against is using the
exact same Python as the Ledger executable. In particular I see this
- bug on OS X systems where boost_python is linked against the default
+ bug on macOS systems where boost_python is linked against the default
Python, while Ledger is linked against the version provided by MacPorts.
Or vice versa.
diff --git a/README.md b/README.md
index 3498a2d6..0ddec293 100644
--- a/README.md
+++ b/README.md
@@ -84,19 +84,9 @@ Dependency | Version (or greater)
[lcov] | 1.6 _optional_, for `make report`, used with `/./acprep gcov`
[sloccount] | 2.26 _optional_, for `make sloc`
-And for building the outdated `release/2.6.3` branch:
+### macOS
-Dependency | Version
------------|--------
-[GMP] | 4.2.2
-[pcre] | 7.7
-[libofx] | 0.8.3 _optional_
-[expat] | 2.0.1 _optional_
-[libxml2] | 2.7.2 _optional_
-
-### Mac OS X
-
-You can use [Homebrew] or [MacPorts] to install Ledger easily on OS X.
+You can use [Homebrew] or [MacPorts] to install Ledger easily on macOS.
#### 1. Homebrew
@@ -111,7 +101,7 @@ If you to want to startup python, use the following command:
#### 2. MacPorts
-If you build stuff using MacPorts on OS X, as I do, here is what you would
+If you build stuff using MacPorts on macOS, as I do, here is what you would
run:
$ sudo port install -f cmake python26 \
@@ -142,12 +132,9 @@ Or, for Ubuntu 12.04:
### Debian
-Debian squeeze (6.0): the version of boost in squeeze is too old
-for ledger and unfortunately no backport is available at the moment.
-
-Debian 7 (wheezy), Debian 8 (jessie), Debian testing (stretch) and Debian
-unstable (sid) contain all components needed to build ledger. You can
-install all required build dependencies using the following command:
+Debian 9 (stretch), Debian 10 (buster), Debian testing and Debian unstable
+(sid) contain all components needed to build ledger. You can install all
+required build dependencies using the following command:
$ sudo apt-get install build-essential cmake autopoint texinfo python-dev \
zlib1g-dev libbz2-dev libgmp3-dev gettext libmpfr-dev \
@@ -166,7 +153,7 @@ footwork for you:
# $HOME/local and build with 2 processes in parallel
$ ./acprep update --boost-suffix=-mt --prefix=$HOME/local -j2
-Please read the contents of `CMakeFiles/CMakeOutput.log` and
+Please read the contents of `CMakeFiles/CMakeOutput.log` and
`CMakeFiles/CMakeError.log` if the configure step fails. Also,
see the `help` subcommand to `acprep`, which explains some of its many
options. It's pretty much the only command I run for configuring, building
diff --git a/acprep b/acprep
index 8d9d67f7..bcbf1308 100755
--- a/acprep
+++ b/acprep
@@ -514,7 +514,7 @@ class PrepareBuild(CommandLineApp):
if system == 'Darwin':
if exists('/opt/local/bin/port'):
- self.log.info('Looks like you are using MacPorts on OS X')
+ self.log.info('Looks like you are using MacPorts on macOS')
packages = [
'sudo', 'port', 'install', '-f',
'automake', 'autoconf', 'libtool',
@@ -531,7 +531,7 @@ class PrepareBuild(CommandLineApp):
self.log.info('Executing: ' + ' '.join(packages))
self.execute(*packages)
elif exists('/usr/local/bin/brew') or exists('/opt/local/bin/brew'):
- self.log.info('Looks like you are using Homebrew on OS X')
+ self.log.info('Looks like you are using Homebrew on macOS')
packages = [
'brew', 'install',
'cmake', 'ninja',
@@ -540,7 +540,7 @@ class PrepareBuild(CommandLineApp):
self.log.info('Executing: ' + ' '.join(packages))
self.execute(*packages)
elif exists('/sw/bin/fink'):
- self.log.info('Looks like you are using Fink on OS X')
+ self.log.info('Looks like you are using Fink on macOS')
self.log.error("I don't know the package names for Fink yet!")
sys.exit(1)
@@ -692,7 +692,7 @@ class PrepareBuild(CommandLineApp):
elif system.startswith('CYGWIN'):
self.log.info('Looks like you are using Cygwin')
- self.log.info('Please install the dependencies manually.')
+ self.log.info('Please install the dependencies manually.')
#########################################################################
# Determine the system's basic configuration #
@@ -1086,7 +1086,7 @@ being 'debug':
debug Debugging and --verify support (default)
opt Full optimizations
gcov Coverage analysis
- gprof Code profiling (for OS X, just use: 'shark -i ledger ...')
+ gprof Code profiling (for macOS, just use: 'shark -i ledger ...')
Next is the optional build PHASE, with 'config' being the default:
diff --git a/appveyor.yml b/appveyor.yml
index 21fec71c..3b2e7267 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -46,5 +46,5 @@ artifacts:
test_script:
- C:\msys64\usr\bin\bash -lc "export MINGW_PREFIX=C:/msys64/mingw32/ CTEST_OUTPUT_ON_FAILURE=1
- PATH=/mingw32/bin:$PATH && cd $APPVEYOR_BUILD_FOLDER &&
+ PATH=/mingw32/bin:$PATH && cd $APPVEYOR_BUILD_FOLDER &&
make test || echo Errors from tests were ignored"
diff --git a/cmake/FindUtfcpp.cmake b/cmake/FindUtfcpp.cmake
index 185a8d88..93828ef4 100644
--- a/cmake/FindUtfcpp.cmake
+++ b/cmake/FindUtfcpp.cmake
@@ -10,7 +10,8 @@ set(UTFCPP_FOUND FALSE)
find_path(UTFCPP_INCLUDE_DIR
NAMES utf8.h
- HINTS "${UTFCPP_PATH}" "${PROJECT_SOURCE_DIR}/lib/utfcpp/v2_0/source"
+ HINTS "${UTFCPP_PATH}"
+ PATHS "${PROJECT_SOURCE_DIR}/lib/utfcpp/v2_0/source"
)
if (UTFCPP_INCLUDE_DIR)
diff --git a/contrib/ledger-completion.bash b/contrib/ledger-completion.bash
index d46b712d..2da44274 100644
--- a/contrib/ledger-completion.bash
+++ b/contrib/ledger-completion.bash
@@ -59,7 +59,7 @@ _ledger()
accounts="Assets Liabilities Equity Revenue Expenses"
case $prev in
- --@(cache|file|init-file|output|pager|price-db|script))
+ --@(cache|file|init-file|output|pager|price-db|script)|-@(f|i|o))
_filedir
return 0
;;
diff --git a/contrib/non-profit-audit-reports/cash-receipts-and-disbursments-journals.plx b/contrib/non-profit-audit-reports/cash-receipts-and-disbursments-journals.plx
index 937d2a45..f2055a62 100755
--- a/contrib/non-profit-audit-reports/cash-receipts-and-disbursments-journals.plx
+++ b/contrib/non-profit-audit-reports/cash-receipts-and-disbursments-journals.plx
@@ -127,7 +127,7 @@ foreach my $typeData ({ name => 'disbursements', query => 'a<=0' },
# I thought '--sort', 'd', '--sort-xact', 'a', should
# have worked below for a good sort. Then I tried
# rather than '--sort', "d,n,a", which didn't work either.
- # I opened a bug: http://bugs.ledger-cli.org/show_bug.cgi?id=901
+ # I opened a bug: https://github.com/ledger/ledger/issues/901
my @csvRegLedgerOpts = ('-f', $tempFile, '-V', '-F', $formatString, '-w', '--sort', 'd',
'-b', $beginDate, '-e', $endDate, 'reg');
diff --git a/default.nix b/default.nix
index 85932da9..a60e607f 100644
--- a/default.nix
+++ b/default.nix
@@ -26,6 +26,11 @@ stdenv.mkDerivation {
cmakeFlags = [ "-DCMAKE_INSTALL_LIBDIR=lib" ];
+ buildPhase = "make -j$NIX_BUILD_CORES";
+ checkPhase = "ctest -j$NIX_BUILD_CORES";
+
+ doCheck = true;
+
meta = {
homepage = "http://ledger-cli.org/";
description = "A double-entry accounting system with a command-line reporting interface";
diff --git a/doc/NEWS b/doc/NEWS
index 0cb4b2b9..c22461ab 100644
--- a/doc/NEWS
+++ b/doc/NEWS
@@ -1,33 +1,66 @@
Ledger NEWS
-* 3.1.2
+* 3.1.2 (unreleased)
-- Increased maximum length for regex from 255 to 4095 (bug #981).
+- Increase maximum length for regex from 255 to 4095 (bug #981)
- Initialize periods from from/since clause rather than earliest
- transaction date (bug #1159).
+ transaction date (bug #1159)
-- Check balance assertions against the amount after the posting (bug #1147).
+- Check balance assertions against the amount after the posting (bug #1147)
-- Allow balance assertions with multiple posts to same account (bug #1187).
+- Allow balance assertions with multiple posts to same account (bug #1187)
-- Fixed period duration of "every X days" and similar statements (bug #370).
+- Fix period duration of "every X days" and similar statements (bug #370)
-- Option --force-color does not require --color anymore (bug #1109).
+- Make option --force-color not require --color anymore (bug #1109)
-- Added quoted_rfc4180 to allow CVS output with RFC 4180 compliant quoting.
+- Add quoted_rfc4180 to allow CVS output with RFC 4180 compliant quoting.
+
+- Add support for --prepend-format in accounts command
+
+- Fix handling of edge cases in trim function (bug #520)
+
+- Fix auto xact posts not getting applied to account total during
+ journal parse (bug #552)
+
+- Transfer null_post flags to generated postings
+
+- Fix segfault when using --market with --group-by
+
+- Use amount_width variable for budget report
+
+- Keep pending items in budgets until the last day they apply
+
+- Fixed bug where .total used in value expressions breaks totals
+
+- Make automated transactions work with assertions (bug #1127)
+
+- Improve parsing of date tokens (bug #1626)
+
+- Don't attempt to invert a value if it's already zero (bug #1703)
+
+- Do not parse user-specified init-file twice
- Python: Removed double quotes from Unicode values.
+- Python: Ensure that parse errors produce useful RuntimeErrors
+
+- Python: Expose journal expand_aliases
+
+- Python: Expose journal_t::register_account
+
+- Improve bash completion
+
- Emacs Lisp files have been moved to https://github.com/ledger/ledger-mode
-- Fixed build under MSYS (32-bit).
+- Fix build under MSYS (32-bit).
-- Fixed build under Cygwin.
+- Fix build under Cygwin.
- Various documentation improvements
-* 3.1.1
+* 3.1.1 (2016-01-11)
- Added a --no-revalued option
@@ -57,7 +90,7 @@
- Add continuous integration (https://travis-ci.org/ledger/ledger)
-* 3.1
+* 3.1 (2014-10-05)
- Changed the definition of cost basis to preserve the original cost basis
when a gain or loss is made (if you bought 1 AAA for $10 and then sold
@@ -154,7 +187,7 @@ features, please see the manual.
2008/07/27 Starting fresh
Assets:Checking $750.00
Equity:Opening Balances
-
+
2008/07/27 Starting fresh
Assets:Checking = $1,000.00
Equity:Adjustments
@@ -164,7 +197,7 @@ features, please see the manual.
2008/07/27 Starting fresh
Assets:Checking $750.00
Equity:Opening Balances
-
+
2008/07/27 Starting fresh
Assets:Checking $250.00
Equity:Adjustments
@@ -183,7 +216,7 @@ features, please see the manual.
2008/07/24 Opening Balance
Assets:Checking = $250.00 ; we force set it
Equity:Opening Balances
-
+
2008/07/24 Opening Balance
Assets:Checking = EC 250.00 ; we force set it again
Equity:Opening Balances
@@ -195,7 +228,7 @@ features, please see the manual.
Assets:Checking = $250.00 ; we force set it again
Assets:Checking EC 100.00 ; and add some EC's
Equity:Opening Balances
-
+
2008/07/24 Opening Balance
Assets:Checking = EC 250.00 ; we force set the EC's
Equity:Opening Balances
@@ -213,15 +246,15 @@ features, please see the manual.
2008/07/24 Opening Balance
Assets:Checking = $100.00
Equity:Opening Balances
-
+
2008/07/30 We spend money, with a known balance afterward
Expenses:Food $20.00
Assets:Checking = $80.00
-
+
2008/07/30 Again we spend money, but this time with all the info
Expenses:Food $20.00
Assets:Checking $-20.00 = $60.00
-
+
2008/07/30 This entry yield an 'unbalanced' error
Expenses:Food $20.00
Assets:Checking $-20.00 = $30.00
diff --git a/doc/ledger.1 b/doc/ledger.1
index 85a9937b..fc4dd3c0 100644
--- a/doc/ledger.1
+++ b/doc/ledger.1
@@ -214,6 +214,9 @@ Note that a comma-separated list of expressions is allowed, in which case each
sorting term is used in order to determine the final ordering. For example,
to search by date and then amount, one would use:
.Dl ledger reg --sort 'date, amount'
+The sort order may be controlled with the '-' sign. For example, to sort in
+reverse chronological order:
+.Dl ledger reg --sort '-date'
.It Fl \-tail Ar number
Only show the last
.Ar number
diff --git a/doc/ledger3.texi b/doc/ledger3.texi
index a955be9a..9f9a1095 100644
--- a/doc/ledger3.texi
+++ b/doc/ledger3.texi
@@ -427,7 +427,7 @@ accounting problems.
Once such tutorial, specifically designed for non-profit charities that seek
to use Ledger, can be found at
-@url{https://k.sfconservancy.org/npo-ledger-cli} (with a copy on GitHub also
+@url{https://k.sfconservancy.org/NPO-Accounting/npo-ledger-cli} (with a copy on GitHub also
available at @url{https://github.com/conservancy/npo-ledger-cli/}). If
you're looking for information about how to use Ledger's tagging system to
handle invoicing, track expenses by program targets, and other such concepts,
@@ -1384,10 +1384,11 @@ account:
(Funds:School) $-100.00
@end smallexample
-When reports are generated, by default they'll appear in terms of the
-funds. In this case, you will likely want to mask out your
-@samp{Assets} account, because otherwise the balance won't make much
-sense:
+The use of round brackets creates a virtual posting without ensuring
+a balance to zero. When reports are generated, by default they'll
+appear in terms of the funds. In this case, you will likely want to
+mask out your @samp{Assets} account, because otherwise the balance
+won't make much sense:
@smallexample @c command:396F24E
$ ledger --no-total bal not ^Assets
@@ -2255,6 +2256,41 @@ Would result in all postings going into
@samp{Personal:Expenses:Groceries} and @samp{Personal:Assets:Checking}
until an @samp{end apply account} directive was found.
+@item apply fixed
+@findex fixed
+@cindex fixated prices
+@c instance_t::fixed_directive in textual.cc
+
+A fixed block is used to set fixated prices (@pxref{Fixated prices and
+costs}) for a series of transactions. It's purely a typing saver, for
+use when entering many transactions with fixated prices.
+
+Thus, the following:
+
+@smallexample @c input:validate
+apply fixed CAD $0.90
+2012-04-10 Lunch in Canada
+ Assets:Wallet -15.50 CAD
+ Expenses:Food 15.50 CAD
+
+2012-04-11 Second day Dinner in Canada
+ Assets:Wallet -25.75 CAD
+ Expenses:Food 25.75 CAD
+end apply fixed
+@end smallexample
+
+is equivalent to this:
+
+@smallexample @c input:validate
+2012-04-10 Lunch in Canada
+ Assets:Wallet -15.50 CAD @{=$0.90@}
+ Expenses:Food 15.50 CAD @{=$0.90@}
+
+2012-04-11 Second day Dinner in Canada
+ Assets:Wallet -25.75 CAD @{=$0.90@}
+ Expenses:Food 25.75 CAD @{=$0.90@}
+@end smallexample
+
@item alias
@findex alias
@cindex account, alias
@@ -2402,6 +2438,7 @@ commodity $
note American Dollars
format $1,000.00
nomarket
+ alias USD
default
@end smallexample
@@ -2416,6 +2453,9 @@ provide the ``canonical'' representation.
The @code{nomarket} sub-directive states that the commodity's price
should never be auto-downloaded.
+The @code{alias} sub-directive states that any commodity matching this
+symbol is to use the commodity declared in this block.
+
The @code{default} sub-directive marks this as the ``default'' commodity.
@item define
@@ -2436,59 +2476,18 @@ The posting will have a cost of $400.
@item end
@findex end
@c instance_t::end_directive in textual.cc
-Closes block commands like @code{tag} or @code{comment}.
+Closes block commands like @code{apply} or @code{comment}.
@item expr
@findex expr
@c instance_t::expr_directive in textual.cc
-@item fixed
-@findex fixed
-@cindex fixated prices
-@c instance_t::fixed_directive in textual.cc
-
-A fixed block is used to set fixated prices (@pxref{Fixated prices and
-costs}) for a series of transactions. It's purely a typing saver, for
-use when entering many transactions with fixated prices.
-
-Thus, the following:
-
-@smallexample @c input:validate
-fixed CAD $0.90
-2012-04-10 Lunch in Canada
- Assets:Wallet -15.50 CAD
- Expenses:Food 15.50 CAD
-
-2012-04-11 Second day Dinner in Canada
- Assets:Wallet -25.75 CAD
- Expenses:Food 25.75 CAD
-endfixed CAD
-@end smallexample
-
-is equivalent to this:
-
-@smallexample @c input:validate
-2012-04-10 Lunch in Canada
- Assets:Wallet -15.50 CAD @{=$0.90@}
- Expenses:Food 15.50 CAD @{=$0.90@}
-
-2012-04-11 Second day Dinner in Canada
- Assets:Wallet -25.75 CAD @{=$0.90@}
- Expenses:Food 25.75 CAD @{=$0.90@}
-@end smallexample
-
-Note that ending a @code{fixed} is done differently than other
-directives, as @code{fixed} is closed with an @code{endfixed} (i.e.,
-there is @emph{no space} between @code{end} and @code{fixed}).
-
-For the moment, users may wish to study
-@uref{http://bugs.ledger-cli.org/show_bug.cgi?id=789, Bug Report 789}
-before using the @code{fixed} directive in production.
-
@item include
@findex include
@c instance_t::include_directive in textual.cc
-Include the stated file as if it were part of the current file.
+Include the stated file as if it were part of the current file. The file
+name can contain a wildcard (@samp{*}) to refer to multiple files (e.g.
+@samp{bank/*.ledger}).
@item payee
@findex payee
@@ -2914,7 +2913,7 @@ you a place to put those codes:
A transaction can have a ``state'': cleared, pending, or uncleared. The
default is uncleared. To mark a transaction cleared, put an asterisk
-@samp{*} before the payee, after the date or code:
+@samp{*} after the date, before the code or payee:
@smallexample @c input:validate
2012-03-10 * KFC
@@ -3105,6 +3104,13 @@ it appears as:
This shows that they are all in the same transaction (which is why the
date is not repeated), but they have different payees now.
+If using the @option{--strict} or @option{--pedantic} options, you must
+declare this tag to avoid warnings and errors.
+
+The payee name used with the tag is not enforced by the
+@option{--check-payees} option, due to a bug:
+@url{https://github.com/ledger/ledger/issues/556}.
+
@node Metadata values, Typed metadata, Metadata tags, Metadata
@subsection Metadata values
@@ -3189,9 +3195,9 @@ look ``harder'') rather than parentheses:
@node Expression amounts, Balance verification, Virtual postings, Transactions
@section Expression amounts
-An amount is usually a numerical figure with an (optional) commodity,
-but it can also be any value expression. To indicate this, surround
-the amount expression with parentheses:
+An amount is a numerical figure with a commodity, but it can also be
+any value expression. To indicate this, surround the amount
+expression with parentheses:
@smallexample @c input:validate
2012-03-10 * KFC
@@ -3232,6 +3238,45 @@ A balance assertion has this general form:
This simply asserts that after subtracting $20.00 from Assets:Cash,
that the resulting total matches $500.00. If not, it is an error.
+The assertion has an effect only on the specified commodity. If an account has
+multiple commodities, then only the one asserted is verified:
+
+@smallexample
+2012-03-10 KFC New York
+ Expenses:Food $20.00
+ Assets:Cash $-20.00 = $500.00
+
+2012-03-11 KFC Montreal
+ Expenses:Food 15.00 CAD
+ Assets:Cash -15.00 CAD = $500.00
+@end smallexample
+
+In this case, the amount in USD of cash (which has not changed) is validated.
+Nothing is asserted about the current amount of Canadian dollars in @samp{Asset:Cash}.
+
+@subsubsection Special assertion value 0
+
+The only value that can be asserted without a commodity is @samp{0}.
+This results in a cross-commodities assertion, which makes it possible to
+assert that an account is totally empty.
+
+@smallexample
+2012-03-09 Fill Wallet
+ Revenue $20.00
+ Revenue 15.00 CAD
+ Assets:Cash
+
+2012-03-10 KFC New York
+ Expenses:Food $20.00
+ Assets:Cash $-20.00
+
+2012-03-11 KFC Montreal
+ Expenses:Food 15.00 CAD
+ Assets:Cash -15.00 CAD = 0
+@end smallexample
+
+The last transaction will assert that we are out of cash of any sort.
+
@node Balance assignments, Resetting a balance, Balance assertions, Balance verification
@subsection Balance assignments
@@ -3508,12 +3553,12 @@ Plus, it comes with dangers. This works fine:
2012-04-10 My Broker
Assets:Brokerage:Cash $375.00
- Assets:Brokerage -5 AAPL @{$50.00@} @@ $375.00
+ Assets:Brokerage -5 AAPL @{$50.00@} @@@@ $375.00
Income:Capital Gains $-125.00
2012-04-10 My Broker
Assets:Brokerage:Cash $375.00
- Assets:Brokerage -5 AAPL @{$50.00@} @@ $375.00
+ Assets:Brokerage -5 AAPL @{$50.00@} @@@@ $375.00
Income:Capital Gains $-125.00
@end smallexample
@@ -3527,12 +3572,12 @@ But this does not do what you might expect:
2012-04-10 My Broker
Assets:Brokerage:Cash $375.00
- Assets:Brokerage -5 AAPL @{@{$500.00@}@} @@ $375.00
+ Assets:Brokerage -5 AAPL @{@{$500.00@}@} @@@@ $375.00
Income:Capital Gains $-125.00
2012-04-10 My Broker
Assets:Brokerage:Cash $375.00
- Assets:Brokerage -5 AAPL @{@{$500.00@}@} @@ $375.00
+ Assets:Brokerage -5 AAPL @{@{$500.00@}@} @@@@ $375.00
Income:Capital Gains $-125.00
@end smallexample
@@ -3608,7 +3653,7 @@ expressions):
@smallexample
2012-04-10 My Broker
Assets:Brokerage:Cash $375.00
- Assets:Brokerage -5 AAPL @{$50.00@} [2012-04-10] @@ $375.00
+ Assets:Brokerage -5 AAPL @{$50.00@} [2012-04-10] @@@@ $375.00
Income:Capital Gains $-125.00
@end smallexample
@@ -3625,7 +3670,7 @@ indicate a virtual cost:
@smallexample
2012-04-10 My Broker
Assets:Brokerage:Cash $375.00
- Assets:Brokerage -5 AAPL @{$50.00@} [2012-04-10] (Oh my!) @@ $375.00
+ Assets:Brokerage -5 AAPL @{$50.00@} [2012-04-10] (Oh my!) @@@@ $375.00
Income:Capital Gains $-125.00
@end smallexample
@@ -8091,8 +8136,13 @@ posting.
A regular expression that matches against a transaction's payee name.
@item %/REGEX/
-@itemx tag(REGEX)
-A regular expression that matches against a transaction's tags.
+@itemx expr has_tag(/REGEX/)
+@itemx expr has_tag('TAG')
+A regular expression (REGEX) or string (TAG) that checks for the tags of
+a transaction.
+
+@item tag(REGEX) =~ /REGEX/
+A regular expression that matches a transaction's tags against its values.
@item expr date =~ /REGEX/
Useful for specifying a date in plain terms. For example, you could say
@@ -8161,6 +8211,19 @@ posting.
A regular expression that matches against the transaction code (the
text that occurs between parentheses before the payee).
+@item expr any(KEYWORD =~ /REGEX/)
+The @command{any} keyword is used to specify that at least one posting of
+the transaction must match the expression in brackets. For example,
+@samp{ledger -f d reg expr "any(account =~ /Assets:/)"} can be used to
+display all transactions which involve at least one @samp{Assets:}
+account.
+
+@item expr all(KEYWORD =~ /REGEX/)
+The @command{all} keyword is used to specify that all postings of a
+transactions must match the expression in brackets. For example,
+@samp{ledger -f d reg expr "all(account =~ /Assets:/)"} can be used to
+display all transactions where all accounts are @samp{Assets:}.
+
@end table
The @command{query} command can be used to see how Ledger interprets
@@ -9218,7 +9281,7 @@ looking at its @code{xact} member:
last_xact = None
for post in ledger.read_journal("sample.dat").query(""):
if post.xact != last_xact:
- for post in post.xact.posts:
+ for post in post.xact.posts():
print "Transferring %s to/from %s" % (post.amount,
post.account)
last_xact = post.xact
diff --git a/src/amount.cc b/src/amount.cc
index 05145f87..c6463b2b 100644
--- a/src/amount.cc
+++ b/src/amount.cc
@@ -604,7 +604,9 @@ void amount_t::in_place_invert()
throw_(amount_error, _("Cannot invert an uninitialized amount"));
_dup();
- mpq_inv(MP(quantity), MP(quantity));
+
+ if (sign() != 0)
+ mpq_inv(MP(quantity), MP(quantity));
}
void amount_t::in_place_round()
diff --git a/src/context.h b/src/context.h
index 4a7a5441..0af59930 100644
--- a/src/context.h
+++ b/src/context.h
@@ -70,6 +70,7 @@ public:
std::size_t errors;
std::size_t count;
std::size_t sequence;
+ std::string last;
explicit parse_context_t(const path& cwd)
: current_directory(cwd), master(NULL), scope(NULL),
diff --git a/src/error.h b/src/error.h
index a628c1e9..bc9953cd 100644
--- a/src/error.h
+++ b/src/error.h
@@ -95,8 +95,9 @@ string source_context(const path& file,
struct error_count {
std::size_t count;
- explicit error_count(std::size_t _count) : count(_count) {}
- const char * what() const { return ""; }
+ std::string message;
+ explicit error_count(std::size_t _count, std::string _msg) : count(_count), message(_msg) {}
+ const char * what() const { return message.c_str(); }
};
} // namespace ledger
diff --git a/src/filters.cc b/src/filters.cc
index 4e9e633a..3dfd2327 100644
--- a/src/filters.cc
+++ b/src/filters.cc
@@ -1245,19 +1245,34 @@ void generate_posts::add_post(const date_interval_t& period, post_t& post)
void budget_posts::report_budget_items(const date_t& date)
{
+ { // Cleanup pending items that finished before date
+ // We have to keep them until the last day they apply because operator() needs them to see if a
+ // posting is budgeted or not
+ std::list<pending_posts_list::iterator> posts_to_erase;
+ for (pending_posts_list::iterator i = pending_posts.begin(); i != pending_posts.end(); i++) {
+ pending_posts_list::value_type& pair(*i);
+ if (pair.first.finish && ! pair.first.start && pair.first.finish < date) {
+ posts_to_erase.push_back(i);
+ }
+ }
+ foreach (pending_posts_list::iterator& i, posts_to_erase)
+ pending_posts.erase(i);
+ }
+
if (pending_posts.size() == 0)
return;
bool reported;
do {
- std::list<pending_posts_list::iterator> posts_to_erase;
-
reported = false;
for (pending_posts_list::iterator i = pending_posts.begin();
i != pending_posts.end();
i++) {
pending_posts_list::value_type& pair(*i);
+ if (pair.first.finish && ! pair.first.start)
+ continue; // skip expired posts
+
optional<date_t> begin = pair.first.start;
if (! begin) {
optional<date_t> range_begin;
@@ -1285,9 +1300,6 @@ void budget_posts::report_budget_items(const date_t& date)
post_t& post = *pair.second;
++pair.first;
- if (! pair.first.start)
- posts_to_erase.push_back(i);
-
DEBUG("budget.generate", "Reporting budget for "
<< post.reported_account()->fullname());
@@ -1312,9 +1324,6 @@ void budget_posts::report_budget_items(const date_t& date)
reported = true;
}
}
-
- foreach (pending_posts_list::iterator& i, posts_to_erase)
- pending_posts.erase(i);
} while (reported);
}
diff --git a/src/global.h b/src/global.h
index 01ad60cc..8f8266ac 100644
--- a/src/global.h
+++ b/src/global.h
@@ -166,7 +166,7 @@ See LICENSE file included with the distribution for details and disclaimer.");
OPTION_(global_scope_t, version, DO() { // -v
parent->show_version_info(std::cout);
- throw error_count(0); // exit immediately
+ throw error_count(0, ""); // exit immediately
});
};
diff --git a/src/output.cc b/src/output.cc
index c2fa83ac..09d3ad9e 100644
--- a/src/output.cc
+++ b/src/output.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003-2018, John Wiegley. All rights reserved.
+ * Copyright (c) 2003-2019, John Wiegley. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@@ -284,8 +284,9 @@ void report_accounts::flush()
std::ostream& out(report.output_stream);
format_t prepend_format;
std::size_t prepend_width;
+ bool do_prepend_format;
- if (report.HANDLED(prepend_format_)) {
+ if ((do_prepend_format = report.HANDLED(prepend_format_))) {
prepend_format.parse_format(report.HANDLER(prepend_format_).str());
prepend_width = report.HANDLED(prepend_width_)
? lexical_cast<std::size_t>(report.HANDLER(prepend_width_).str())
@@ -293,7 +294,7 @@ void report_accounts::flush()
}
foreach (accounts_pair& entry, accounts) {
- if (prepend_format) {
+ if (do_prepend_format) {
bind_scope_t bound_scope(report, *entry.first);
out.width(static_cast<std::streamsize>(prepend_width));
out << prepend_format(bound_scope);
diff --git a/src/print.cc b/src/print.cc
index 9fa75eab..92323777 100644
--- a/src/print.cc
+++ b/src/print.cc
@@ -103,11 +103,13 @@ namespace {
void print_xact(report_t& report, std::ostream& out, xact_t& xact)
{
format_type_t format_type = FMT_WRITTEN;
+ string format_str;
optional<const char *> format;
if (report.HANDLED(date_format_)) {
format_type = FMT_CUSTOM;
- format = report.HANDLER(date_format_).str().c_str();
+ format_str = report.HANDLER(date_format_).str();
+ format = format_str.c_str();
}
std::ostringstream buf;
diff --git a/src/session.cc b/src/session.cc
index e95123e8..427850d9 100644
--- a/src/session.cc
+++ b/src/session.cc
@@ -76,7 +76,7 @@ std::size_t session_t::read_data(const string& master_account)
file = path(home_var) / ".ledger";
if (! file.empty() && exists(file))
- HANDLER(file_).data_files.insert(file);
+ HANDLER(file_).data_files.push_back(file);
else
throw_(parse_error, "No journal file was specified (please use -f)");
@@ -214,7 +214,7 @@ journal_t * session_t::read_journal_files()
journal_t * session_t::read_journal(const path& pathname)
{
HANDLER(file_).data_files.clear();
- HANDLER(file_).data_files.insert(pathname);
+ HANDLER(file_).data_files.push_back(pathname);
return read_journal_files();
}
diff --git a/src/session.h b/src/session.h
index 47732a3d..4dce3816 100644
--- a/src/session.h
+++ b/src/session.h
@@ -153,14 +153,14 @@ public:
OPTION__
(session_t, file_, // -f
- std::set<path COMMA ComparePaths> data_files;
+ std::list<path> data_files;
CTOR(session_t, file_) {}
DO_(str) {
if (parent->flush_on_next_data_file) {
data_files.clear();
parent->flush_on_next_data_file = false;
}
- data_files.insert(str);
+ data_files.push_back(str);
});
OPTION_(session_t, input_date_format_, DO_(str) {
diff --git a/src/system.hh.in b/src/system.hh.in
index 38ac1e63..799bb47c 100644
--- a/src/system.hh.in
+++ b/src/system.hh.in
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003-2018, John Wiegley. All rights reserved.
+ * Copyright (c) 2003-2019, John Wiegley. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@@ -60,8 +60,6 @@
#define HAVE_EDIT @HAVE_EDIT@
#define HAVE_GETTEXT @HAVE_GETTEXT@
-#cmakedefine HAVE_ACCESS
-#cmakedefine HAVE_REALPATH
#cmakedefine HAVE_GETPWUID
#cmakedefine HAVE_GETPWNAM
#cmakedefine HAVE_IOCTL
diff --git a/src/textual.cc b/src/textual.cc
index 8fbc5c08..3416073b 100644
--- a/src/textual.cc
+++ b/src/textual.cc
@@ -282,6 +282,10 @@ void instance_t::parse()
std::cerr << _("Error: ") << err.what() << std::endl;
context.errors++;
+ if (! current_context.empty())
+ context.last = current_context + "\n" + err.what();
+ else
+ context.last = err.what();
}
}
@@ -1644,29 +1648,30 @@ post_t * instance_t::parse_post(char * line,
}
DEBUG("textual.parse", "line " << context.linenum << ": "
- << "POST assign: parsed amt = " << *post->assigned_amount);
+ << "POST assign: parsed balance amount = " << *post->assigned_amount);
- amount_t& amt(*post->assigned_amount);
+ const amount_t& amt(*post->assigned_amount);
value_t account_total
(post->account->amount().strip_annotations(keep_details_t()));
DEBUG("post.assign", "line " << context.linenum << ": "
<< "account balance = " << account_total);
- DEBUG("post.assign",
- "line " << context.linenum << ": " << "post amount = " << amt);
+ DEBUG("post.assign", "line " << context.linenum << ": "
+ << "post amount = " << amt << " (is_zero = " << amt.is_zero() << ")");
- amount_t diff = amt;
+ balance_t diff = amt;
switch (account_total.type()) {
case value_t::AMOUNT:
- if (account_total.as_amount().commodity_ptr() == diff.commodity_ptr())
- diff -= account_total.as_amount();
+ diff -= account_total.as_amount();
+ DEBUG("textual.parse", "line " << context.linenum << ": "
+ << "Subtracting amount " << account_total.as_amount() << " from diff, yielding " << diff);
break;
case value_t::BALANCE:
- if (optional<amount_t> comm_bal =
- account_total.as_balance().commodity_amount(amt.commodity()))
- diff -= *comm_bal;
+ diff -= account_total.as_balance();
+ DEBUG("textual.parse", "line " << context.linenum << ": "
+ << "Subtracting balance " << account_total.as_balance() << " from diff, yielding " << diff);
break;
default:
@@ -1680,18 +1685,34 @@ post_t * instance_t::parse_post(char * line,
// Subtract amounts from previous posts to this account in the xact.
for (post_t* p : xact->posts) {
- if (p->account == post->account &&
- p->amount.commodity_ptr() == diff.commodity_ptr()) {
+ if (p->account == post->account) {
diff -= p->amount;
DEBUG("textual.parse", "line " << context.linenum << ": "
- << "Subtract " << p->amount << ", diff = " << diff);
+ << "Subtracting " << p->amount << ", diff = " << diff);
}
}
+ // If amt has a commodity, restrict balancing to that. Otherwise, it's the blanket '0' and
+ // check that all of them are zero.
+ if (amt.has_commodity()) {
+ DEBUG("textual.parse", "line " << context.linenum << ": "
+ << "Finding commodity " << amt.commodity() << " (" << amt << ") in balance " << diff);
+ optional<amount_t> wanted_commodity = diff.commodity_amount(amt.commodity());
+ if (!wanted_commodity) {
+ diff = amt - amt; // this is '0' with the correct commodity.
+ } else {
+ diff = *wanted_commodity;
+ }
+ DEBUG("textual.parse", "line " << context.linenum << ": "
+ << "Diff is now " << diff);
+ }
+
if (post->amount.is_null()) {
// balance assignment
if (! diff.is_zero()) {
- post->amount = diff;
+ // This will fail if there are more than 1 commodity in diff, which is wanted,
+ // as amount cannot store more than 1 commodity.
+ post->amount = diff.to_amount();
DEBUG("textual.parse", "line " << context.linenum << ": "
<< "Overwrite null posting");
}
@@ -1699,10 +1720,11 @@ post_t * instance_t::parse_post(char * line,
// balance assertion
diff -= post->amount;
if (! no_assertions && ! diff.is_zero()) {
- amount_t tot = amt - diff;
+ balance_t tot = -diff + amt;
+ DEBUG("textual.parse", "Balance assertion: off by " << diff << " (expected to see " << tot << ")");
throw_(parse_error,
_f("Balance assertion off by %1% (expected to see %2%)")
- % diff % tot);
+ % diff.to_string() % tot.to_string());
}
}
@@ -2012,7 +2034,8 @@ std::size_t journal_t::read_textual(parse_context_stack_t& context_stack)
TRACE_FINISH(parsing_total, 1);
if (context_stack.get_current().errors > 0)
- throw error_count(context_stack.get_current().errors);
+ throw error_count(context_stack.get_current().errors,
+ context_stack.get_current().last);
return context_stack.get_current().count;
}
diff --git a/src/times.cc b/src/times.cc
index 8e4df020..eda71ae7 100644
--- a/src/times.cc
+++ b/src/times.cc
@@ -420,7 +420,6 @@ class date_parser_t
TOK_DASH,
TOK_DOT,
- TOK_A_YEAR,
TOK_A_MONTH,
TOK_A_WDAY,
@@ -512,9 +511,6 @@ class date_parser_t
case TOK_SLASH: return "/";
case TOK_DASH: return "-";
case TOK_DOT: return ".";
- case TOK_A_YEAR:
- out << boost::get<date_specifier_t::year_type>(*value);
- break;
case TOK_A_MONTH:
out << date_specifier_t::month_type
(boost::get<date_time::months_of_year>(*value));
@@ -566,7 +562,6 @@ class date_parser_t
case TOK_SLASH: out << "TOK_SLASH"; break;
case TOK_DASH: out << "TOK_DASH"; break;
case TOK_DOT: out << "TOK_DOT"; break;
- case TOK_A_YEAR: out << "TOK_A_YEAR"; break;
case TOK_A_MONTH: out << "TOK_A_MONTH"; break;
case TOK_A_WDAY: out << "TOK_A_WDAY"; break;
case TOK_AGO: out << "TOK_AGO"; break;
@@ -727,7 +722,11 @@ void date_parser_t::determine_when(date_parser_t::lexer_t::token_t& tok,
when += gregorian::days(amount * adjust);
break;
default:
- specifier.day = date_specifier_t::day_type(amount);
+ if (amount > 31) {
+ specifier.year = date_specifier_t::year_type(amount);
+ } else {
+ specifier.day = date_specifier_t::day_type(amount);
+ }
break;
}
@@ -832,16 +831,13 @@ void date_parser_t::determine_when(date_parser_t::lexer_t::token_t& tok,
break;
}
- case lexer_t::token_t::TOK_A_YEAR:
- specifier.year = boost::get<date_specifier_t::year_type>(*tok.value);
- break;
case lexer_t::token_t::TOK_A_MONTH:
specifier.month =
date_specifier_t::month_type
(boost::get<date_time::months_of_year>(*tok.value));
tok = lexer.peek_token();
switch (tok.kind) {
- case lexer_t::token_t::TOK_A_YEAR:
+ case lexer_t::token_t::TOK_INT:
specifier.year = boost::get<date_specifier_t::year_type>(*tok.value);
break;
case lexer_t::token_t::END_REACHED:
@@ -898,12 +894,6 @@ date_interval_t date_parser_t::parse()
determine_when(tok, *inclusion_specifier);
break;
- case lexer_t::token_t::TOK_A_YEAR:
- if (! inclusion_specifier)
- inclusion_specifier = date_specifier_t();
- determine_when(tok, *inclusion_specifier);
- break;
-
case lexer_t::token_t::TOK_A_MONTH:
if (! inclusion_specifier)
inclusion_specifier = date_specifier_t();
@@ -1612,13 +1602,8 @@ date_parser_t::lexer_t::token_t date_parser_t::lexer_t::next_token()
if (! term.empty()) {
if (std::isdigit(term[0])) {
- if (term.length() == 4)
- return token_t(token_t::TOK_A_YEAR,
- token_t::content_t
- (lexical_cast<date_specifier_t::year_type>(term)));
- else
- return token_t(token_t::TOK_INT,
- token_t::content_t(lexical_cast<unsigned short>(term)));
+ return token_t(token_t::TOK_INT,
+ token_t::content_t(lexical_cast<unsigned short>(term)));
}
else if (std::isalpha(term[0])) {
to_lower(term);
diff --git a/src/utils.h b/src/utils.h
index b21dff7a..c9146dd7 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003-2018, John Wiegley. All rights reserved.
+ * Copyright (c) 2003-2019, John Wiegley. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@@ -44,7 +44,11 @@
#ifndef _UTILS_H
#define _UTILS_H
+#if (BOOST_VERSION >= 106600)
+#include <boost/uuid/detail/sha1.hpp>
+#else
#include <boost/uuid/sha1.hpp>
+#endif
/**
* @name Default values
@@ -496,10 +500,6 @@ inline T& downcast(U& object) {
path resolve_path(const path& pathname);
-#ifdef HAVE_REALPATH
-extern "C" char * realpath(const char *, char resolved_path[]);
-#endif
-
inline const string& either_or(const string& first,
const string& second) {
return first.empty() ? second : first;
diff --git a/src/xact.cc b/src/xact.cc
index d29072d4..5df9ebc5 100644
--- a/src/xact.cc
+++ b/src/xact.cc
@@ -396,9 +396,9 @@ bool xact_base_t::finalize()
}
if (post->has_flags(POST_DEFERRED))
- post->account->add_deferred_post(id(), post);
- else
- post->account->add_post(post);
+ post->account->add_deferred_post(id(), post);
+ else
+ post->account->add_post(post);
post->xdata().add_flags(POST_EXT_VISITED);
post->account->xdata().add_flags(ACCOUNT_EXT_VISITED);
@@ -806,6 +806,10 @@ void auto_xact_t::extend_xact(xact_base_t& xact, parse_context_t& context)
xact.add_post(new_post);
new_post->account->add_post(new_post);
+ // Add flags so this post updates the account balance
+ new_post->xdata().add_flags(POST_EXT_VISITED);
+ new_post->account->xdata().add_flags(ACCOUNT_EXT_VISITED);
+
if (new_post->must_balance())
needs_further_verification = true;
}
diff --git a/test/python/JournalTest.py b/test/python/JournalTest.py
index e65c671d..2565ede8 100644
--- a/test/python/JournalTest.py
+++ b/test/python/JournalTest.py
@@ -22,6 +22,24 @@ class JournalTestCase(unittest.TestCase):
for post in journal.query("food"):
self.assertEqual(str(post.account), "Expenses:Food")
self.assertEqual(post.amount, Amount("$21.34"))
+
+ def testParseError(self):
+ # TODO: ledger spits out parse errors to standard out.
+ # This should not happen, especially when the error
+ # has already been captured by a Python exception.
+ def fun():
+ read_journal_from_string("""
+2012-03-01 KFC
+ Expenses:Food rsnetnirsnti
+ Assets:Cash
+""")
+ self.assertRaises(RuntimeError, fun)
+ try:
+ fun()
+ except RuntimeError as e:
+ self.assertEquals(str(e).splitlines()[-1],
+ "No quantity specified for amount")
+
def suite():
return unittest.TestLoader().loadTestsFromTestCase(JournalTestCase)
diff --git a/test/regress/1127.test b/test/regress/1127.test
new file mode 100644
index 00000000..905401f8
--- /dev/null
+++ b/test/regress/1127.test
@@ -0,0 +1,15 @@
+; Test that automated transactions are added to accounts soon enough
+; for assertions to work.
+
+= expr account =~ /^Assets/
+ (Foo) 1
+
+2018-06-09 Something
+ Assets $100
+ Equity
+
+2018-06-09 Assert amount added by automated transaction
+ [Foo] = $100
+test bal Foo
+ $100 Foo
+end test
diff --git a/test/regress/1187_5.test b/test/regress/1187_5.test
new file mode 100644
index 00000000..4aa5fdd8
--- /dev/null
+++ b/test/regress/1187_5.test
@@ -0,0 +1,36 @@
+2013/12/01 * Initial State
+ Crédit:Viseca:MasterCard P1 -618.50 CHF
+ Crédit:Viseca:MasterCard P2 -52.10 CHF
+ Equity:Opening Balances
+
+2013/12/15 * Buy Some Chocolate
+ Dépenses:Nourriture 19.00 EUR ; #1
+ Crédit:Viseca:MasterCard P1
+
+2013/12/15 * Buy Some Chocolate
+ Crédit:Viseca:MasterCard P1 18.00 EUR ; #2
+ Recettes:Erreurs
+
+2013/12/23 * Facture Viseca
+ Crédit:Viseca:MasterCard P2 52.10 CHF = 0 ; #3
+ Crédit:Viseca:MasterCard P1 618.50 CHF = 0 CHF ; #4
+ Dépenses:Frais:Gestion Comptes 1.50 CHF
+ Crédit:Viseca -672.10 CHF
+
+2014/01/03 * Facture Viseca
+ Crédit:Viseca 672.10 CHF = 0
+ Actif:Comptes:CP courant
+
+test bal
+ -672.10 CHF Actif:Comptes:CP courant
+ -1.00 EUR Crédit:Viseca
+ -1.00 EUR MasterCard P1
+ 1.50 CHF
+ 19.00 EUR Dépenses
+ 1.50 CHF Frais:Gestion Comptes
+ 19.00 EUR Nourriture
+ 670.60 CHF Equity:Opening Balances
+ -18.00 EUR Recettes:Erreurs
+--------------------
+ 0
+end test
diff --git a/test/regress/1626.test b/test/regress/1626.test
new file mode 100644
index 00000000..89ae80f8
--- /dev/null
+++ b/test/regress/1626.test
@@ -0,0 +1,28 @@
+test period every 1000 years from 1 Sep 2011 to 30 May 2012 --now=2018-06-10
+--- Period expression tokens ---
+TOK_EVERY: every
+TOK_INT: 1000
+TOK_YEARS: years
+TOK_SINCE: since
+TOK_INT: 1
+TOK_A_MONTH: Sep
+TOK_INT: 2011
+TOK_UNTIL: until
+TOK_INT: 30
+TOK_A_MONTH: May
+TOK_INT: 2012
+END_REACHED: <EOF>
+
+--- Before stabilization ---
+ range: from day 1 to day 30
+duration: 1000 years
+
+--- After stabilization ---
+ range: from day 1 to day 30
+ start: 18-Jan-01
+ finish: 18-Jan-30
+duration: 1000 years
+
+--- Sample dates in range (max. 20) ---
+ 1: 18-Jan-01 -- 18-Jan-29
+end test
diff --git a/test/regress/1703.test b/test/regress/1703.test
new file mode 100644
index 00000000..983b79be
--- /dev/null
+++ b/test/regress/1703.test
@@ -0,0 +1,11 @@
+
+P 2018-10-31 MultifundosPlus R$0
+
+2017-05-03 * Test
+ Assets:A 1 AAA @ R$ 3000
+ Assets:B
+
+test reg assets:a -V --now 2018-12-31
+17-May-03 Test Assets:A R$3000 R$3000
+end test
+
diff --git a/test/regress/7F3650FD.test b/test/regress/7F3650FD.test
index f0498ddb..0ccfe644 100644
--- a/test/regress/7F3650FD.test
+++ b/test/regress/7F3650FD.test
@@ -68,7 +68,7 @@ end test
test period --now=2010/11/01 2009
--- Period expression tokens ---
-TOK_A_YEAR: 2009
+TOK_INT: 2009
END_REACHED: <EOF>
--- Before stabilization ---
diff --git a/test/regress/BBFA1759.test b/test/regress/BBFA1759.test
index 7a402d0c..5df7ecb2 100644
--- a/test/regress/BBFA1759.test
+++ b/test/regress/BBFA1759.test
@@ -2,7 +2,7 @@
test period june 2008
--- Period expression tokens ---
TOK_A_MONTH: Jun
-TOK_A_YEAR: 2008
+TOK_INT: 2008
END_REACHED: <EOF>
--- Before stabilization ---
diff --git a/test/regress/fix-missing-trans-in-last-budget-period.test b/test/regress/fix-missing-trans-in-last-budget-period.test
new file mode 100644
index 00000000..163a0540
--- /dev/null
+++ b/test/regress/fix-missing-trans-in-last-budget-period.test
@@ -0,0 +1,79 @@
+= ~ ^A
+ [Balance] 1
+ [Budget:$account] -1
+
+~ Monthly from 2014/01 to 2014/12/31
+ [Budget:A] 100.00 USD
+ [Balance]
+
+~ Monthly from 2014/01 to 2014/12/31
+ [Budget:Z] 100.00 USD
+ [Balance]
+
+2014/10/01 toto0
+ [Budget:A:B] 0.01 USD
+ [Balance]
+
+2014/11/01 toto1
+ A:B 51.00 USD
+ Cash
+
+2014/11/02 toto2
+ A:B 52.00 USD
+ Cash
+
+2014/11/03 toto3
+ A:B 53.00 USD
+ Cash
+
+2014/11/04 toto4
+ A:B 54.00 USD
+ Cash
+
+2014/12/08 toto5
+ A:B 55.00 USD
+ Cash
+
+2014/12/09 toto6
+ A:B 56.00 USD
+ Cash
+
+2014/12/10 toto7
+ A:B 57.00 USD
+ Cash
+
+2014/12/11 toto8
+ A:B 58.00 USD
+ Cash
+
+2014/12/12 toto9
+ A:B 59.00 USD
+ Cash
+
+2014/12/12 toto9
+ C 59.00 USD
+ Cash
+
+2015/01/12 toto10
+ A:B 59.00 USD
+ Cash
+
+test reg --budget -b 2014/10 -e 2015/02 --columns 80 --date-format "%F" reg ^Bu
+2014-10-01 Budget transaction [Budget:A] -100.00 USD -100.00 USD
+2014-10-01 Budget transaction [Budget:Z] -100.00 USD -200.00 USD
+2014-10-01 toto0 [Budget:A] 0.01 USD -199.99 USD
+2014-11-01 Budget transaction [Budget:A] -100.00 USD -299.99 USD
+2014-11-01 Budget transaction [Budget:Z] -100.00 USD -399.99 USD
+2014-11-01 toto1 [Budget:A] -51.00 USD -450.99 USD
+2014-11-02 toto2 [Budget:A] -52.00 USD -502.99 USD
+2014-11-03 toto3 [Budget:A] -53.00 USD -555.99 USD
+2014-11-04 toto4 [Budget:A] -54.00 USD -609.99 USD
+2014-12-01 Budget transaction [Budget:A] -100.00 USD -709.99 USD
+2014-12-01 Budget transaction [Budget:Z] -100.00 USD -809.99 USD
+2014-12-08 toto5 [Budget:A] -55.00 USD -864.99 USD
+2014-12-09 toto6 [Budget:A] -56.00 USD -920.99 USD
+2014-12-10 toto7 [Budget:A] -57.00 USD -977.99 USD
+2014-12-11 toto8 [Budget:A] -58.00 USD -1035.99 USD
+2014-12-12 toto9 [Budget:A] -59.00 USD -1094.99 USD
+2015-01-12 toto10 [Budget:A] -59.00 USD -1153.99 USD
+end test