diff options
100 files changed, 8407 insertions, 2190 deletions
@@ -12,7 +12,6 @@ .libs/ ABOUT-NLS BaselineTests -Doxyfile.gen Makefile Makefile.am Makefile.in @@ -34,6 +33,7 @@ configure configure.ac data_tests depcomp +doc/Doxyfile doc/*.aux doc/*.cp doc/*.fn @@ -48,6 +48,7 @@ doc/.dirstamp doc/html/ doc/latex/ doc/ledger.info +doc/ledger3.info doc/refman.pdf doc/report/ elisp-comp @@ -97,3 +98,10 @@ system.hh system.hh.[gp]ch* Ledger*.dmg Ledger*.sh +# Files that generated by running ./demo.sh in contrib/non-profit-audit-reports +contrib/non-profit-audit-reports/tests/chart-of-accounts.txt +contrib/non-profit-audit-reports/tests/general-ledger.csv +contrib/non-profit-audit-reports/tests/general-ledger.ods +contrib/non-profit-audit-reports/tests/general-ledger.txt +contrib/non-profit-audit-reports/tests/MANIFEST +contrib/non-profit-audit-reports/general-ledger.zip diff --git a/.gitmodules b/.gitmodules index 26e5425c..ff480831 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "lib/utfcpp"] path = lib/utfcpp - url = http://github.com/jwiegley/utfcpp.git + url = http://github.com/ledger/utfcpp.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 487fe429..f18df69a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 2.8.5) -project(Ledger) +PROJECT(ledger) set(Ledger_VERSION_MAJOR 3) set(Ledger_VERSION_MINOR 0) @@ -18,7 +18,8 @@ option(DISABLE_ASSERTS "Build without any internal consistency checks" OFF) option(BUILD_DEBUG "Build support for runtime debugging" OFF) option(BUILD_LIBRARY "Build and install Ledger as a library" ON) -option(BUILD_DOCS "Build and install documentation" OFF) +option(BUILD_DOCS "Build and install documentation" ON) +option(BUILD_WEB_DOCS "Build version of documentation suitable for viewing online" OFF) option(BUILD_EMACSLISP "Build and install ledger-mode for Emacs" OFF) if(BUILD_DEBUG) @@ -45,6 +46,7 @@ endif() find_package(PythonInterp) # Used for running tests if(USE_PYTHON) + set(Python_ADDITIONAL_VERSIONS 2.7 2.6) find_package(PythonLibs) if(PYTHONLIBS_FOUND) set(BOOST_PYTHON python) @@ -274,9 +276,7 @@ elseif(CMAKE_CXX_COMPILER MATCHES "g\\+\\+") endif() add_subdirectory(src) -if(BUILD_DOCS) - add_subdirectory(doc) -endif() +add_subdirectory(doc) if(BUILD_EMACSLISP) add_subdirectory(lisp) endif() @@ -5,9 +5,12 @@ To build this code after doing a Git clone, run: $ ./acprep update -If you try to configure and build on your own, you are almost certainly going -to run into problems. In future, you can run this command again and again, -and it will keep you updated to the very latest version. +If anything goes wrong, see "COMMON CONFIGURE/BUILD PROBLEMS" below. + +If you try to configure and build without running acprep first, you are +almost certainly going to run into problems. In future, you can run +'acprep update' again and again, and it will keep you updated to the +very latest version. I further recommend building both debug and optimized versions of Ledger, in a subdirectory of your source tree named 'build' (which acprep will manage for @@ -17,12 +20,17 @@ you, you simply need to create it): $ ./acprep opt make $ ./acprep debug make -Now install the optimized version, but know that you have 'build/debug/ledger' -available for testing and more useful bug reports. +Now install the optimized version: -=============================================================================== + $ cd build/ledger/opt + $ sudo make install - IF YOU HAVE CONFIGURE OR BUILD PROBLEMS +but know that you have 'build/ledger/debug' available for testing and +for more useful bug reports. + +=============================================================================== + COMMON CONFIGURE/BUILD PROBLEMS +=============================================================================== To build and install Ledger requires several dependencies on various platforms. You can install these dependencies very simply for most of them @@ -41,8 +49,9 @@ With the contents of config.log, and the output from acprep --debug update, it's usually fairly obvious where things have gone astray. =============================================================================== - F.A.Q. +=============================================================================== + - Q: The build fails saying it can't find utf8.h @@ -50,6 +59,29 @@ it's usually fairly obvious where things have gone astray. ---------------------------------------------------------------------- + - Q: './acprep update' gives errors or './acprep dependencies' fails + + A: You're probably missing some dependency libraries. If you tried + './acprep dependencies' already and that didn't solve the problem, + then you may need to install dependencies by hand. On a Debian + GNU/Linux system (or Debian-based system such as Ubuntu), something + like this should work (as root): + + # aptitude update + # for name in \ + cmake libboost-dev libboost-date-time-dev libboost-doc \ + libboost-dbg libboost-filesystem-dev libboost-graph-dev \ + libboost-iostreams-dev libboost-program-options-dev \ + libboost-python-dev libboost-regex-dev \ + libboost-serialization-dev libboost-signals-dev \ + libboost-test-dev libboost-thread-dev libboost-wave-dev \ + libmpfr-dev libmpfr-dbg libmpfr-doc; \ + do \ + aptitude install ${name}; \ + done + + ---------------------------------------------------------------------- + - Q: Configure fails saying it can't find boost_regex A: Look in config.log and search for "boost_regex", then scroll down a bit @@ -68,7 +100,10 @@ it's usually fairly obvious where things have gone astray. - Q: Configure fails saying it can't find MPFR A: You need MPFR version 2.4.0 or higher. This version does not come with - most Debian distributions, so you will need to build it. + most Debian distributions, so you will need to build it. The + relevant packages are 'libmpfr-dev' and 'libmpfr-dbg'. See also + the question above about what to do if './acprep update' gives + errors or './acprep dependencies' fails. ---------------------------------------------------------------------- @@ -114,3 +149,42 @@ it's usually fairly obvious where things have gone astray. A: This can happen for the same reason as above. It can also happen if you have ICU support enabled. This is a bug I'm still trying to track down. + + ---------------------------------------------------------------------- + + - Q: My distribution has versions of Boost and/or CMake that are too old for + Ledger. How do I build my own Boost and/or CMake binaries that will + work properly with Ledger? Thereafter, how do I configure Ledger + properly to use those newly built verisons of Boost and/or CMake? + + A: Here's commands that one user used to make this work, for Boost 1.51.0 + on Debian GNU/Linux 6.0.x (aka Debian squeeze). It's likely to work ok + for other versions of Boost as well. YMMV on other distributions and/or + other Debian distribution versions, though. + + # Preparing and building Boost 1.51.0 + + $ cd /somewhere/you/want/to/build/boost + $ wget -N http://iweb.dl.sourceforge.net/project/boost/boost/1.51.0/boost_1_51_0.tar.bz2 + $ tar xvf boost_1_51_0.tar.bz2 + $ cd boost_1_51_0 + $ ./bootstrap.sh + $ ./b2 --build-type=complete --layout=tagged --prefix=/where/you/want/boost/installed + $ ./b2 --build-type=complete --layout=tagged --prefix=/where/you/want/boost/installed install + + # Preparing and building CMake 2.8.8 + + $ cd /somewhere/you/want/to/build/cmake + $ wget -N http://www.cmake.org/files/v2.8/cmake-2.8.8.tar.gz + $ tar xvf cmake-2.8.8.tar.gz + $ cd cmake-2.8.8 + $ ./configure --prefix=/where/you/want/cmake/installed/ + $ make + $ make install + + # Building Ledger using the CMake and/or Boost as installed above + + $ cd /path/to/ledger/sources + $ env PATH=/where/you/want/cmake/installed/bin:$PATH BOOST_ROOT=/where/you/want/boost/installed ./acprep --prefix=/where/you/want/ledger/installed --debug --python config + $ env PATH=/where/you/want/cmake/installed/bin:$PATH BOOST_ROOT=/where/you/want/boost/installed ./acprep --prefix=/where/you/want/ledger/installed --debug --python make + $ env PATH=/where/you/want/cmake/installed/bin:$PATH BOOST_ROOT=/where/you/want/boost/installed ./acprep --prefix=/where/you/want/ledger/installed --debug --python install @@ -300,6 +300,9 @@ class PrepareBuild(CommandLineApp): action="store", dest="compiler", help='Use the Clang C++ compiler') + op.add_option('-N', '--ninja', action='store_true', dest='use_ninja', + default=False, + help='Use ninja to build, rather than make') op.add_option('', '--no-git', action='store_true', dest='no_git', default=False, help='Do not call out to Git; useful for offline builds') @@ -658,20 +661,9 @@ class PrepareBuild(CommandLineApp): def setup_for_johnw(self): self.configure_args.append('-DCMAKE_EXPORT_COMPILE_COMMANDS:BOOL=ON') - self.configure_args.append('-DBoost_USE_MULTITHREADED:BOOL=OFF') if not self.options.compiler: - self.configure_args.append('-DCMAKE_CXX_COMPILER:PATH=/usr/local/stow/clang-3.1/bin/clang++') - - self.CXXFLAGS.append('-Qunused-arguments') - self.CXXFLAGS.append('-nostdlibinc') - self.CXXFLAGS.append('-isystem') - self.CXXFLAGS.append('/usr/local/include/c++/v1') - self.CXXFLAGS.append('-isystem') - self.CXXFLAGS.append('/usr/include') - self.CXXFLAGS.append('-stdlib=libc++') - self.CXXFLAGS.append('-Wl,/usr/local/lib/libc++.dylib') - self.CXXFLAGS.append('-Wno-disabled-macro-expansion') + self.configure_args.append('-DCMAKE_CXX_COMPILER:PATH=/usr/local/bin/clang++') if self.current_flavor == 'opt': self.configure_args.append('-DCMAKE_CXX_FLAGS_RELEASE:STRING=-O4') @@ -680,31 +672,13 @@ class PrepareBuild(CommandLineApp): # self.CXXFLAGS.append('-g -O1 -faddress-sanitizer') # self.LDFLAGS.append('-g -O1 -faddress-sanitizer') - self.configure_args.append('-DCMAKE_INCLUDE_PATH:STRING=/usr/local/include;/opt/local/include') - self.configure_args.append('-DCMAKE_LIBRARY_PATH:STRING=/usr/local/lib;/opt/local/lib') - - self.configure_args.append('-DBOOST_ROOT=/usr/local') - self.configure_args.append('-DBOOST_INCLUDEDIR=/usr/local/include/boost-1_50') - self.configure_args.append('-DBoost_COMPILER=-clang-darwin') - - self.configure_args.append(self.source_dir) - - elif self.options.compiler == "icc": - self.configure_args.append('-DCMAKE_AR:PATH=/opt/intel/bin/xiar') - self.configure_args.append('-DCMAKE_CXX_COMPILER:PATH=/opt/intel/bin/icc') - if self.current_flavor == 'opt': - self.configure_args.append('-DCMAKE_CXX_FLAGS:STRING=-fast') - self.configure_args.append('-DCMAKE_CXX_LINK_FLAGS:STRING=-fast') - self.configure_args.append('-DCMAKE_INCLUDE_PATH:STRING=/opt/local/include') - self.configure_args.append('-DCMAKE_LIBRARY_PATH:STRING=/opt/local/lib') - self.configure_args.append('-DBOOST_ROOT=/opt/local') self.configure_args.append(self.source_dir) else: self.configure_args.append('-DCMAKE_CXX_COMPILER:PATH=' + self.options.compiler) - self.configure_args.append('-DCMAKE_INCLUDE_PATH:STRING=/opt/local/include') - self.configure_args.append('-DCMAKE_LIBRARY_PATH:STRING=/opt/local/lib') - self.configure_args.append('-DBOOST_ROOT=/opt/local') + self.configure_args.append('-DCMAKE_INCLUDE_PATH:STRING=/usr/local/include') + self.configure_args.append('-DCMAKE_LIBRARY_PATH:STRING=/usr/local/lib') + self.configure_args.append('-DBOOST_ROOT=/usr/local') self.configure_args.append(self.source_dir) def setup_for_system(self): @@ -718,6 +692,9 @@ class PrepareBuild(CommandLineApp): if self.options.no_python: self.configure_args.remove('-DUSE_PYTHON=1') + if self.options.use_ninja: + self.configure_args.append('-GNinja') + if exists('/Users/johnw/Projects/ledger/plan/TODO'): self.setup_for_johnw() @@ -847,7 +824,7 @@ class PrepareBuild(CommandLineApp): self.options.boost_include) if self.prefix_directory(): - conf_args.append('-DCMAKE_PREFIX_PATH=%s' % self.prefix_directory()) + conf_args.append('-DCMAKE_INSTALL_PREFIX=%s' % self.prefix_directory()) return (environ, conf_args + self.configure_args) @@ -910,7 +887,6 @@ class PrepareBuild(CommandLineApp): if self.options.jobs > 1 and self.current_flavor != 'gcov': make_args.append('-j%d' % self.options.jobs) - make_args.append('ARGS=-j%d' % self.options.jobs) if self.options.verbose: make_args.append('VERBOSE=1') @@ -926,18 +902,29 @@ class PrepareBuild(CommandLineApp): self.log.debug('Changing directory to ' + build_dir) os.chdir(build_dir) - self.execute(*(['make'] + make_args)) + self.execute(*(['ninja' if self.options.use_ninja else 'make'] + + make_args)) finally: os.chdir(self.source_dir) def phase_check(self, *args): self.log.info('Executing phase: update') - self.phase_make(*(['check'] + list(args))) + build_dir = self.ensure(self.build_directory()) + try: + self.log.debug('Changing directory to ' + build_dir) + os.chdir(build_dir) + + make_args = list(args) + if self.options.jobs > 1: + make_args.append('-j%d' % self.options.jobs) + + self.execute(*(['ctest'] + list(make_args))) + finally: + os.chdir(self.source_dir) def phase_update(self, *args): self.log.info('Executing phase: update') self.phase_pull() - #self.phase_check(*args) self.phase_make(*args) ######################################################################### diff --git a/contrib/non-profit-audit-reports/GPLv3 b/contrib/non-profit-audit-reports/GPLv3 new file mode 100644 index 00000000..94a9ed02 --- /dev/null +++ b/contrib/non-profit-audit-reports/GPLv3 @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + <program> Copyright (C) <year> <name of author> + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<http://www.gnu.org/licenses/>. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +<http://www.gnu.org/philosophy/why-not-lgpl.html>. diff --git a/contrib/non-profit-audit-reports/LICENSE b/contrib/non-profit-audit-reports/LICENSE new file mode 100644 index 00000000..c8c63924 --- /dev/null +++ b/contrib/non-profit-audit-reports/LICENSE @@ -0,0 +1,14 @@ +Contents under contrib/non-profit-audit-reports/ are licensed GPLv3-or-later. + +The GPLv3-or-later licensing of the contents of this directory does not, +to our knowledge and belief, impact the licensing of any other part of +Ledger. Parts of the files herein are likely derivative works of the rest +of Ledger, but these works are under this subdirectory are not, to our +knowledge, used, imported, included, copied, etc. into other parts of the +codebase. + +In short, this is a small application written to use Ledger like a +library, particularly via its Python API interface. It derives from +Ledger, but Ledger does not, to our knowledge, derive from it. + +We are not lawyers and this is not legal advice. diff --git a/contrib/non-profit-audit-reports/README b/contrib/non-profit-audit-reports/README new file mode 100644 index 00000000..b4897f21 --- /dev/null +++ b/contrib/non-profit-audit-reports/README @@ -0,0 +1,100 @@ +README + +This document provides backround on the enclosed example + +Demo +---- +To run the demo do +./demo.sh + +Which should generate the following files in tests/ + chart-of-accounts.txt + general-ledger.txt + general-ledger.csv + general-ledger.ods + +And a final, "portable" zip file with the spreadsheet in + general-ledger.zip + +It *should* be possible to copy general-ledger.zip to another system, +unzip it, open general-ledger.ods in Libre Office and have the relative +links resolve correctly. + +NOTE: Export to PDF should also work. + + +Known Dependencies +------------------ +ledger (3.0) +python (2.x) +zip +libdate-manip-perl +libmath-gmp-perl + + +Temporary Hacks +--------------- +Due to an urgent project deadline the ooolib2 directory +represents some fixes to: + http://ooolib.sourceforge.net/ + +The proper version of this library can be installed on Debian systems with +# apt-get install python-ooolib + +Compare the deltas to the current version with +# diff -u /usr/share/pyshared/ooolib/__init__.py ooolib2/__init__.py + +Note also that the csv2ods.py treats columns 4 and 5 (numbering from 1) of the csv +magically. If column 4 contains a non-empty string which is not 'Receipt' +then it is interpreted as a relative path of an artifact to link to. +Similary for column 5 and 'Invoice'. + + +Sample PDF files +---------------- +The sample PDF files were created as follows: + +paps --font="Courier 12" --paper letter --top-margin=18 tests/Projects/Foo/Expenses/hosting/AprilHostingReceipt.txt | ps2pdf - tests/Projects/Foo/Expenses/hosting/AprilHostingReceipt.pdf + +paps --font="Courier 12" --paper letter --top-margin=18 tests/Financial/Invoices/Invoice20110510.txt | ps2pdf - tests/Financial/Invoices/Invoice20110510.pdf + + +Resources +--------- +ooolib + http://ooolib.sourceforge.net/ + +LIBPF + probably does not replace ooolib + http://wp.libpf.com/?p=82 + +Libre Office Calc Guide (contains function reference) + https://www.libreoffice.org/get-help/documentation/ + +Libre Office API + http://api.libreoffice.org/examples/examples.html + http://api.libreoffice.org/examples/DevelopersGuide/examples.html + +OpenOffice Developers Guide + http://wiki.openoffice.org/wiki/Documentation/DevGuide/OpenOffice.org_Developers_Guide + +Spreadsheet Documents + http://wiki.openoffice.org/wiki/Documentation/DevGuide/Spreadsheets/Spreadsheet_Documents + +How to correctly create ODF documents using zip +(Do NOT do this, use ooolib instead) + http://www.jejik.com/articles/2010/03/how_to_correctly_create_odf_documents_using_zip/ + +Line Breaks + fo:break-before="page" + http://books.evc-cit.info/oobook/ch03.html#page-content-section + +ODF Validator + http://opendocumentfellowship.com/validator + +Editing Hyperlinks + http://help.libreoffice.org/Common/Editing_Hyperlinks + +Perl OODoc +NOTE: a replacement for POD, not ooolib + http://search.cpan.org/dist/OpenOffice-OODoc/ 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 new file mode 100755 index 00000000..2ad18a44 --- /dev/null +++ b/contrib/non-profit-audit-reports/cash-receipts-and-disbursments-journals.plx @@ -0,0 +1,147 @@ +#!/usr/bin/perl +# cash-receipts-and-disbursments-journals -*- Perl -*- +# +# Script to generate a General Ledger report that accountants like +# using Ledger. +# +# Copyright (C) 2011, 2012 Bradley M. Kuhn +# +# This program gives you software freedom; you can copy, modify, convey, +# and/or redistribute it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program in a file called 'GPLv3'. If not, write to the: +# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor +# Boston, MA 02110-1301, USA. + +use strict; +use warnings; + +use Math::BigFloat; +use Date::Manip; +use File::Temp qw/tempfile/; + +my $LEDGER_CMD = "/usr/local/bin/ledger"; + +my $ACCT_WIDTH = 75; + +sub ParseNumber($) { + $_[0] =~ s/,//g; + return Math::BigFloat->new($_[0]); +} + +sub LedgerAcctToFilename($) { + my $x = $_[0]; + $x =~ s/ /-/g; + $x =~ s/:/-/g; + return $x; +} + +Math::BigFloat->precision(-2); +my $ZERO = Math::BigFloat->new("0.00"); + +if (@ARGV < 2) { + print STDERR "usage: $0 <BEGIN_DATE> <END_DATE> <OTHER_LEDGER_OPTS>\n"; + exit 1; +} + +my($beginDate, $endDate, @otherLedgerOpts) = @ARGV; + +my(@chartOfAccountsOpts) = ('-V', '-F', "%150A\n", '-w', '-s', + '-b', $beginDate, '-e', $endDate, @otherLedgerOpts, 'reg'); + +open(CHART_DATA, "-|", $LEDGER_CMD, @chartOfAccountsOpts) + or die "Unable to run $LEDGER_CMD @chartOfAccountsOpts: $!"; + +my @accounts; +while (my $line = <CHART_DATA>) { + chomp $line; + $line =~ s/^\s*//; $line =~ s/\s*$//; + push(@accounts, $line); + +} +close(CHART_DATA); die "error reading ledger output for chart of accounts: $!" unless $? == 0; + +my $formattedEndDate = new Date::Manip::Date; +die "badly formatted end date, $endDate" if $formattedEndDate->parse($endDate); +my $oneDayLess = new Date::Manip::Delta; +die "bad one day less" if $oneDayLess->parse("- 1 day"); +$formattedEndDate = $formattedEndDate->calc($oneDayLess); +$formattedEndDate = $formattedEndDate->printf("%Y/%m/%d"); + +foreach my $acct (@accounts) { + next unless ($acct =~ /^(?:Assets|Liabilities)/); + + my $acctFilename = LedgerAcctToFilename($acct); + + foreach my $typeData ({ name => 'disbursements', query => 'a<=0' }, + { name => 'receipts', query => 'a>0' }) { + my $fileNameBase = $acctFilename . '-' . $typeData->{name}; + + open(TEXT_OUT, ">", "$fileNameBase.txt") or die "unable to open $fileNameBase.txt: $!"; + open(CSV_OUT, ">", "$fileNameBase.csv") or die "unable to open $fileNameBase.csv: $!"; + + print TEXT_OUT "\n\nACCOUNT: $acct\nFROM: $beginDate TO $formattedEndDate\n\n"; + print CSV_OUT "\n\"ACCOUNT:\",\"$acct\"\n\"PERIOD START:\",\"$beginDate\"\n\"PERIOD END:\",\"$formattedEndDate\"\n"; + print CSV_OUT '"DATE","CHECK NUM","NAME","ACCOUNT","AMOUNT"'; + + my @entryLedgerOpts = ('-l', $typeData->{query}, + '-b', $beginDate, '-e', $endDate, @otherLedgerOpts, 'print', $acct); + + open(ENTRY_DATA, "-|", $LEDGER_CMD, @entryLedgerOpts) + or die "Unable to run $LEDGER_CMD @entryLedgerOpts: $!"; + + my($tempFH, $tempFile) = tempfile("cashreportsXXXXXXXX", TMPDIR => 1); + + while (my $line = <ENTRY_DATA>) { print $tempFH $line; } + close(ENTRY_DATA); die "Error reading ledger output for entries: $!" unless $? == 0; + $tempFH->close() or die "Error writing ledger output for entries to temp file, $tempFile: $!"; + + goto SKIP_REGISTER_COMMANDS if (-z $tempFile); + + my @txtRegLedgerOpts = ('-f', $tempFile, '-V', '-F', + "%(date) %-.70P %-.10C %-.80A %18t\n", '-w', '--sort', 'd', + '-b', $beginDate, '-e', $endDate, 'reg'); + + my $formatString = '\n"%(date)","%C","%P","%A","%t"\n%/"","","","%A","%t"'; + foreach my $tagField (qw/Receipt Invoice Statement Contract PurchaseOrder Approval Check IncomeDistributionAnalysis CurrencyRate/) { + print CSV_OUT ',"', $tagField, '"'; + $formatString .= ',"link:%(tag(\'' . $tagField . '\'))"'; + } + $formatString .= "\n"; + print CSV_OUT "\n"; + my @csvRegLedgerOpts = ('-f', $tempFile, '-V', '-F', $formatString, '-w', '--sort', 'd', + '-b', $beginDate, '-e', $endDate, 'reg'); + + + open(TXT_DATA, "-|", $LEDGER_CMD, @txtRegLedgerOpts) + or die "unable to run ledger command for $fileNameBase.txt: $!"; + + while (my $line = <TXT_DATA>) { print TEXT_OUT $line; } + close(TEXT_OUT); die "Error read write text out to $fileNameBase.txt: $!" unless $? == 0; + + open(CSV_DATA, "-|", $LEDGER_CMD, @csvRegLedgerOpts) + or die "unable to run ledger command for $fileNameBase.csv: $!"; + + while (my $line = <CSV_DATA>) { $line =~ s/"link:"/""/g; print CSV_OUT $line; } + close(CSV_DATA); die "Error read from csv ledger command $!" unless $? == 0; + + SKIP_REGISTER_COMMANDS: + close(TXT_DATA); die "Error read from txt ledger command $!" unless $? == 0; + close(CSV_OUT); die "Error read write csv out to $fileNameBase.csv: $!" unless $? == 0; + unlink($tempFile); + } +} +############################################################################### +# +# Local variables: +# compile-command: "perl -c cash-receipts-and-disbursments-journals.plx" +# End: + diff --git a/contrib/non-profit-audit-reports/csv2ods.py b/contrib/non-profit-audit-reports/csv2ods.py new file mode 100755 index 00000000..8b880648 --- /dev/null +++ b/contrib/non-profit-audit-reports/csv2ods.py @@ -0,0 +1,128 @@ +#!/usr/bin/python +# csv2ods.py +# Convert example csv file to ods +# +# Copyright (c) 2012 Tom Marble +# Copyright (c) 2012, 2013 Bradley M. Kuhn +# +# This program gives you software freedom; you can copy, modify, convey, +# and/or redistribute it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program in a file called 'GPLv3'. If not, write to the: +# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor +# Boston, MA 02110-1301, USA. + +import sys, os, os.path, optparse +import csv +import ooolib2 + +def err(msg): + print 'error: %s' % msg + sys.exit(1) + +def csv2ods(csvname, odsname, encoding='', verbose = False): + filesSavedinManifest = {} + + if verbose: + print 'converting from %s to %s' % (csvname, odsname) + doc = ooolib2.Calc() + # add a pagebreak style + style = 'pagebreak' + style_pagebreak = doc.styles.get_next_style('row') + style_data = tuple([style, ('style:row-height', doc.styles.property_row_height)]) + doc.styles.style_config[style_data] = style_pagebreak + # add a currency style + style = 'currency' + style_currency = doc.styles.get_next_style('cell') + style_data = tuple([style]) + doc.styles.style_config[style_data] = style_currency + + row = 1 + csvdir = os.path.dirname(csvname) + if len(csvdir) == 0: + csvdir = '.' + csvfile = open(csvname, 'rb') + reader = csv.reader(csvfile, delimiter=',', quotechar='"') + for fields in reader: + if len(fields) > 0: + for col in range(len(fields)): + val = fields[col] + if encoding != '': + val = unicode(val, 'utf8') + if len(val) > 0 and val[0] == '$': + doc.set_cell_value(col + 1, row, 'currency', val[1:]) + else: + if (len(val) > 0 and val[0:5] == "link:"): + val = val[5:] + linkrel = '../' + val # ../ means remove the name of the *.ods + linkname = os.path.basename(val) # name is just the last component + doc.set_cell_value(col + 1, row, 'link', (linkrel, linkname)) + linkpath = csvdir + '/' + val + + if not val in filesSavedinManifest: + filesSavedinManifest[val] = col + + if not os.path.exists(linkpath): + print "WARNING: link %s DOES NOT EXIST at %s" % (val, linkpath) + if verbose: + if os.path.exists(linkpath): + print 'relative link %s EXISTS at %s' % (val, linkpath) + else: + if val == "pagebreak": + doc.sheets[doc.sheet_index].set_sheet_config(('row', row), style_pagebreak) + else: + doc.set_cell_value(col + 1, row, 'string', val) + else: + # enter an empty string for blank lines + doc.set_cell_value(1, row, 'string', '') + row += 1 + # save manifest file + if filesSavedinManifest.keys() != []: + manifestFH = open("MANIFEST", "a") + manifestFH.write("# Files from %s\n" % odsname) + for file in filesSavedinManifest.keys(): + manifestFH.write("%s\n" % file) + + manifestFH.close() + # Save spreadsheet file. + doc.save(odsname) + +def main(): + program = os.path.basename(sys.argv[0]) + version = '0.1' + parser = optparse.OptionParser(usage='%prog [--help] [--verbose]', + version='%prog ' + version) + parser.add_option('-v', '--verbose', action='store_true', + dest='verbose', + help='provide extra information while processing') + parser.add_option('-c', '--csv', action='store', + help='csv file to process') + parser.add_option('-o', '--ods', action='store', + help='ods output filename') + parser.add_option('-e', '--encoding', action='store', + help='unicode character encoding type') + (options, args) = parser.parse_args() + if len(args) != 0: + parser.error("not expecting extra args") + if not os.path.exists(options.csv): + err('csv does not exist: %s' % options.csv) + if not options.ods: + (root, ext) = os.path.splitext(options.csv) + options.ods = root + '.ods' + if options.verbose: + print '%s: verbose mode on' % program + print 'csv:', options.csv + print 'ods:', options.ods + print 'ods:', options.encoding + csv2ods(options.csv, options.ods, options.encoding, options.verbose) + +if __name__ == '__main__': + main() diff --git a/contrib/non-profit-audit-reports/demo.sh b/contrib/non-profit-audit-reports/demo.sh new file mode 100755 index 00000000..a4b837a6 --- /dev/null +++ b/contrib/non-profit-audit-reports/demo.sh @@ -0,0 +1,45 @@ +#!/bin/sh +# demo.sh +# Demonstrate a non-profit GL export and conversion to ODS + +program=$(basename $0) +dir=$(dirname $0) +cd $dir +dir=$(pwd -P) +export PYTHONPATH=$dir/ooolib2 + +getcsv=$dir/general-ledger-report.plx +csv2ods=$dir/csv2ods.py + +echo "Demonstrating ledger to ODS export in $dir/tests" +cd $dir/tests +sampledata=non-profit-test-data.ledger +echo " based on the sample data in $sampledata" + +$getcsv 2011/03/01 2012/03/01 -f $sampledata +if [ -e general-ledger.csv ]; then + echo "data was exported to: general-ledger.csv" +else + echo "error creating csv file" + exit 1 +fi + +$csv2ods --verbose --csv general-ledger.csv +if [ -e general-ledger.ods ]; then + echo "csv was converted to: general-ledger.ods" +else + echo "error creating ods file" + exit 1 +fi + +echo general-ledger.ods >> MANIFEST + +# create a portable zip file with the spreadsheet +# and the linked artifacts + +echo creating portable zipfile... +cat MANIFEST | zip -@ ../general-ledger.zip + +echo " " +echo "created general-ledger.zip" + diff --git a/contrib/non-profit-audit-reports/fund-report.plx b/contrib/non-profit-audit-reports/fund-report.plx new file mode 100755 index 00000000..ce59da96 --- /dev/null +++ b/contrib/non-profit-audit-reports/fund-report.plx @@ -0,0 +1,235 @@ +#!/usr/bin/perl +# fund-report.plx -*- Perl -*- +# +# Script to generate a Restricted Fund Report. Usefulness of this +# script may be confined to those who track separate funds in their +# accounts by having accounts that match this format: +# /^(Income|Expenses|Unearned Income|(Accrued:[^:]+:):PROJECTNAME/ + +# Conservancy does this because we carefully track fund balances for our +# fiscal sponsored projects. Those who aren't fiscal sponsors won't find +# this report all that useful, I suspect. Note that the name +# "Conservancy" is special-cased in a few places, mainly because our +# "General" fund is called "Conservancy". + +# +# Copyright (C) 2011, 2012, 2013 Bradley M. Kuhn +# +# This program gives you software freedom; you can copy, modify, convey, +# and/or redistribute it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program in a file called 'GPLv3'. If not, write to the: +# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor +# Boston, MA 02110-1301, USA. + +use strict; +use warnings; + +use Math::BigFloat; +use Date::Manip; + +my $LEDGER_CMD = "/usr/local/bin/ledger"; + +my $ACCT_WIDTH = 70; + +sub ParseNumber($) { + $_[0] =~ s/,//g; + return Math::BigFloat->new($_[0]); +} +Math::BigFloat->precision(-2); +my $ZERO = Math::BigFloat->new("0.00"); +my $TWO_CENTS = Math::BigFloat->new("0.02"); + +if (@ARGV < 2) { + print STDERR "usage: $0 <START_DATE> <END_DATE> <LEDGER_OPTIONS>\n"; + exit 1; +} +my($startDate, $endDate, @mainLedgerOptions) = @ARGV; + +my $err; +my $formattedEndDate = UnixDate(DateCalc(ParseDate($endDate), ParseDateDelta("- 1 day"), \$err), + "%Y/%m/%d"); +die "Date calculation error on $endDate" if ($err); +my $formattedStartDate = UnixDate(ParseDate($startDate), "%Y/%m/%d"); +die "Date calculation error on $startDate" if ($err); + +# First, get balances for starting and ending for each fund + +my %funds; + +foreach my $type ('starting', 'ending') { + my(@ledgerOptions) = (@mainLedgerOptions, + '-V', '-X', '$', '-F', "%-.70A %22.108t\n", '-s'); + + if ($type eq 'starting') { + push(@ledgerOptions, '-e', $startDate); + } else { + push(@ledgerOptions,'-e', $endDate); + } + push(@ledgerOptions, 'reg', '/^(Income|Expenses):([^:]+):/'); + + open(LEDGER_FUNDS, "-|", $LEDGER_CMD, @ledgerOptions) + or die "Unable to run $LEDGER_CMD @ledgerOptions: $!"; + + while (my $fundLine = <LEDGER_FUNDS>) { + die "Unable to parse output line from first funds command: \"$fundLine\"" + unless $fundLine =~ /^\s*([^\$]+)\s+\$\s*\s*([\-\d\.\,]+)/; + my($account, $amount) = ($1, $2); + $amount = ParseNumber($amount); + $account =~ s/\s+$//; + next if $account =~ /\<Adjustment\>/ and (abs($amount) <= $TWO_CENTS); + die "Weird account found, $account with amount of $amount in command: @ledgerOptions\n" + unless $account =~ s/^\s*(?:Income|Expenses):([^:]+)://; + $account = $1; + $account = 'General' if $account eq 'Conservancy'; # FIXME: this is a special case for Consrevancy + $funds{$account}{$type} += $amount; + } + close LEDGER_FUNDS; + die "Failure on ledger command @ledgerOptions: $!" unless ($? == 0); +} +foreach my $fund (keys %funds) { + foreach my $type (keys %{$funds{$fund}}) { + $funds{$fund}{$type} = $ZERO - $funds{$fund}{$type}; + } +} +my(@ledgerOptions) = (@mainLedgerOptions, + '-V', '-X', '$', '-F', "%-.70A %22.108t\n", '-w', '-s', + '-b', $startDate, '-e', $endDate, 'reg'); + +my @possibleTypes = ('Income', 'Expenses', 'Unearned Income', 'Retained Earnings', 'Retained Costs', + 'Accrued:Loans Receivable', 'Accrued:Accounts Payable', + 'Accrued:Accounts Receivable', 'Accrued:Expenses'); + +foreach my $type (@possibleTypes) { + foreach my $fund (keys %funds) { + my $query; + $query = ($fund eq 'General') ? "/^${type}:Conservancy/": "/^${type}:$fund/"; + open(LEDGER_INCOME, "-|", $LEDGER_CMD, @ledgerOptions, $query) + or die "Unable to run $LEDGER_CMD for funds: $!"; + $funds{$fund}{$type} = $ZERO; + while (my $line = <LEDGER_INCOME>) { + die "Unable to parse output line from $type line command: $line" + unless $line =~ /^\s*([^\$]+)\s+\$\s*\s*([\-\d\.\,]+)/; + my($account, $amount) = ($1, $2); + $amount = ParseNumber($amount); + $funds{$fund}{$type} += $amount; + } + close LEDGER_INCOME; + die "Failure on ledger command for ${type}:$fund: $!" unless ($? == 0); + } +} + +my %tot; +($tot{Start}, $tot{End}) = ($ZERO, $ZERO); + +my %beforeEndings = ('Income' => 1, 'Expenses' => 1); +my %afterEndings; + +# For other @possibleTypes, build up @fieldsList to just thoes that are present. + +foreach my $fund (keys %funds) { + foreach my $type (@possibleTypes) { + if ($funds{$fund}{$type} != $ZERO) { + if ($type =~ /^(Unearned Income|Accrued)/) { + $afterEndings{$type} = 1; + } else { + $beforeEndings{$type} = 1; + } + } + } +} +my(@beforeEndingFields, @afterEndingFields); + +foreach my $ii (@possibleTypes) { + push(@beforeEndingFields, $ii) if defined $beforeEndings{$ii}; + push(@afterEndingFields, $ii) if defined $afterEndings{$ii}; +} +# Make sure fieldLists present items are zero for those that should be zero. +foreach my $fund (keys %funds) { + foreach my $type ('starting', @beforeEndingFields, 'ending', @afterEndingFields) { + $funds{$fund}{$type} = $ZERO unless defined $funds{$fund}{$type}; + } +} + +print '"RESTRICTED AND GENERAL FUND REPORT",', "\"BEGINNING:\",\"$formattedStartDate\",\"ENDING:\",\"$formattedEndDate\"\n\n"; +print '"FUND","STARTING BALANCE",'; +my @finalPrints; +foreach my $type (@beforeEndingFields) { + $tot{$type} = $ZERO; + my $formattedType = $type; + print "\"$formattedType\","; +} +print '"ENDING BALANCE",""'; +foreach my $type (@afterEndingFields) { + $tot{$type} = $ZERO; + my $formattedType = $type; + $formattedType = "Prepaid Expenses" if $formattedType eq 'Accrued:Expenses'; + $formattedType =~ s/^Accrued://; + print ",\"$formattedType\""; +} +print "\n\n"; + +sub printTotal ($$) { + my($label, $tot) = @_; + print "\"$label\",\"\$$tot->{Start}\","; + foreach my $type (@beforeEndingFields) { + print "\"\$$tot->{$type}\","; + } + print "\"\$$tot->{End}\",\"\""; + foreach my $type (@afterEndingFields) { + print ",\"\$$tot->{$type}\""; + } + print "\n"; +} + +foreach my $fund (sort { + if ($a eq "General") { return 1 } + elsif ($b eq "General") { return -1 } + else { return $a cmp $b } } + keys %funds) { + my $sanityTotal = $funds{$fund}{starting}; + + if ($fund eq 'General') { + print "\n"; + printTotal("Restricted Subtotal", \%tot); + print "\n"; + } + $tot{Start} += $funds{$fund}{starting}; + $tot{End} += $funds{$fund}{ending}; + + print "\"$fund\",\"\$$funds{$fund}{starting}\","; + foreach my $type (@beforeEndingFields) { + print "\"\$$funds{$fund}{$type}\","; + $tot{$type} += $funds{$fund}{$type}; + } + print "\"\$$funds{$fund}{ending}\",\"\""; + foreach my $type (@afterEndingFields) { + print ",\"\$$funds{$fund}{$type}\""; + $tot{$type} += $funds{$fund}{$type}; + } + print "\n"; + # Santity check: + if (abs($funds{$fund}{ending} - + ($funds{$fund}{starting} + - $funds{$fund}{Income} - $funds{$fund}{Expenses})) + > $TWO_CENTS) { + print "$fund FAILED SANITY CHECK: Ending: $funds{$fund}{ending} \n\n\n"; + warn "$fund FAILED SANITY CHECK"; + } +} +print "\n"; +printTotal("OVERALL TOTAL", \%tot); +############################################################################### +# +# Local variables: +# compile-command: "perl -c fund-report.plx" +# End: + diff --git a/contrib/non-profit-audit-reports/general-ledger-report.plx b/contrib/non-profit-audit-reports/general-ledger-report.plx new file mode 100755 index 00000000..1fd0e7ce --- /dev/null +++ b/contrib/non-profit-audit-reports/general-ledger-report.plx @@ -0,0 +1,225 @@ +#!/usr/bin/perl +# general-ledger-report.plx -*- Perl -*- +# +# Script to generate a General Ledger report that accountants like +# using Ledger. +# +# Copyright (C) 2011, 2012, 2013 Bradley M. Kuhn +# Copyright (C) 2012 Tom Marble +# +# This program gives you software freedom; you can copy, modify, convey, +# and/or redistribute it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program in a file called 'GPLv3'. If not, write to the: +# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor +# Boston, MA 02110-1301, USA. + +use strict; +use warnings; + +use Math::BigFloat; +use Date::Manip; + +my $LEDGER_CMD = "/usr/local/bin/ledger"; + +my $ACCT_WIDTH = 75; + +sub ParseNumber($) { + $_[0] =~ s/,//g; + return Math::BigFloat->new($_[0]); +} + +Math::BigFloat->precision(-2); +my $ZERO = Math::BigFloat->new("0.00"); + +if (@ARGV < 3) { + print STDERR "usage: $0 <BEGIN_DATE> <END_DATE> <OTHER_LEDGER_OPTS>\n"; + exit 1; +} + + +open(MANIFEST, ">", "MANIFEST") or die "Unable to open MANIFEST for writing: $!"; + +my($beginDate, $endDate, @otherLedgerOpts) = @ARGV; + +my $formattedEndDate = new Date::Manip::Date; +die "badly formatted end date, $endDate" if $formattedEndDate->parse($endDate); +my $oneDayLess = new Date::Manip::Delta; +die "bad one day less" if $oneDayLess->parse("- 1 day"); +$formattedEndDate = $formattedEndDate->calc($oneDayLess); +$formattedEndDate = $formattedEndDate->printf("%Y/%m/%d"); + +my $formattedBeginDate = new Date::Manip::Date; +die "badly formatted end date, $endDate" if $formattedBeginDate->parse($endDate); +$formattedBeginDate = $formattedBeginDate->printf("%Y/%m/%d"); + + +my(@chartOfAccountsOpts) = ('-V', '-F', "%150A\n", '-w', '-s', + '-b', $beginDate, '-e', $endDate, @otherLedgerOpts, 'reg'); + +open(CHART_DATA, "-|", $LEDGER_CMD, @chartOfAccountsOpts) + or die "Unable to run $LEDGER_CMD @chartOfAccountsOpts: $!"; + +my @accounts; +while (my $line = <CHART_DATA>) { + chomp $line; + next if $line =~ /^\s*\<\s*Adjustment\s*\>\s*$/; + $line =~ s/^\s*//; $line =~ s/\s*$//; + push(@accounts, $line); + +} +close(CHART_DATA); die "error reading ledger output for chart of accounts: $!" unless $? == 0; + +open(CHART_OUTPUT, ">", "chart-of-accounts.csv") or die "unable to write chart-of-accounts.csv: $!"; +print MANIFEST "chart-of-accounts.csv\n"; + +print CHART_OUTPUT "\"CHART OF ACCOUNTS\","; +print CHART_OUTPUT "\"BEGINNING:\",\"$formattedBeginDate\","; +print CHART_OUTPUT "\"ENDING:\",\"$formattedEndDate\"\n"; + +sub preferredAccountSorting ($$) { + if ($_[0] =~ /^Assets/ and $_[1] !~ /^Assets/) { + return -1; + } elsif ($_[1] =~ /^Assets/ and $_[0] !~ /^Assets/) { + return 1; + } elsif ($_[0] =~ /^Liabilities/ and $_[1] !~ /^(Assets|Liabilities)/) { + return -1; + } elsif ($_[1] =~ /^Liabilities/ and $_[0] !~ /^(Assets|Liabilities)/) { + return 1; + } elsif ($_[0] =~ /^(Accrued:[^:]+Receivable)/ and $_[1] !~ /^(Assets|Liabilities|Accrued:[^:]+Receivable)/) { + return -1; + } elsif ($_[1] =~ /^(Accrued:[^:]+Receivable)/ and $_[0] !~ /^(Assets|Liabilities|Accrued:[^:]+Receivable)/) { + return 1; + } elsif ($_[0] =~ /^(Accrued)/ and $_[1] !~ /^(Assets|Liabilities|Accrued)/) { + return -1; + } elsif ($_[1] =~ /^(Accrued)/ and $_[0] !~ /^(Assets|Liabilities|Accrued)/) { + return 1; + } elsif ($_[0] =~ /^(Unearned Income)/ and $_[1] !~ /^(Assets|Liabilities|Accrued|Unearned Income)/) { + return -1; + } elsif ($_[1] =~ /^(Unearned Income)/ and $_[0] !~ /^(Assets|Liabilities|Accrued|Unearned Income)/) { + return 1; + } elsif ($_[0] =~ /^Income/ and $_[1] !~ /^(Assets|Liabilities|Accrued|Unearned Income|Income)/) { + return -1; + } elsif ($_[1] =~ /^Income/ and $_[0] !~ /^(Assets|Liabilities|Accrued|Unearned Income|Income)/) { + return 1; + } elsif ($_[0] =~ /^Expense/ and $_[1] !~ /^(Assets|Liabilities|Accrued|Income|Unearned Income|Expense)/) { + return -1; + } elsif ($_[1] =~ /^Expense/ and $_[0] !~ /^(Assets|Liabilities|Accrued|Income|Unearned Income|Expense)/) { + return 1; + } else { + return $_[0] cmp $_[1]; + } +} + + +my @sortedAccounts; +foreach my $acct ( sort preferredAccountSorting @accounts) { + print CHART_OUTPUT "\"$acct\"\n"; + push(@sortedAccounts, $acct); +} +close(CHART_OUTPUT); die "error writing to chart-of-accounts.txt: $!" unless $? == 0; + +my %commands = ( + 'totalEnd' => [ $LEDGER_CMD, @otherLedgerOpts, '-V', '-X', '$', + '-e', $endDate, '-F', '%-.80A %22.108t\n', '-s', + 'reg' ], + 'totalBegin' => [ $LEDGER_CMD, @otherLedgerOpts, '-V', '-X', '$', + '-e', $beginDate, '-F', '%-.80A %22.108t\n', + '-s', 'reg' ]); + +my %balanceData; + +foreach my $id (keys %commands) { + my(@command) = @{$commands{$id}}; + + open(FILE, "-|", @command) or die "unable to run command ledger command: @command: $!"; + + foreach my $line (<FILE>) { + die "Unable to parse output line from balance data $id command: $line" + unless $line =~ /^\s*([^\$]+)\s+\$\s*([\-\d\.\,]+)/; + my($account, $amount) = ($1, $2); + $amount = ParseNumber($amount); + $account =~ s/\s+$//; + next if $account =~ /\<Adjustment\>/ and (abs($amount) <= 0.02); + next if $account =~ /^Equity:/; # Stupid auto-account made by ledger. + $balanceData{$id}{$account} = $amount; + } + close FILE; + die "unable to run balance data ledger command, @command: $!" unless ($? == 0); +} + +open(GL_TEXT_OUT, ">", "general-ledger.txt") or die "unable to write general-ledger.txt: $!"; +print MANIFEST "general-ledger.txt\n"; +open(GL_CSV_OUT, ">", "general-ledger.csv") or die "unable to write general-ledger.csv: $!"; +print MANIFEST "general-ledger.csv\n"; + +my %manifest; +foreach my $acct (@sortedAccounts) { + print GL_TEXT_OUT "\n\nACCOUNT: $acct\nFROM: $beginDate TO $formattedEndDate\n\n"; + my @acctLedgerOpts = ('-V', '-F', + "%(date) %-.10C %-.80P %-.80N %18t %18T\n", '-w', '--sort', 'd', + '-b', $beginDate, '-e', $endDate, @otherLedgerOpts, 'reg', $acct); + open(GL_TEXT_DATA, "-|", $LEDGER_CMD, @acctLedgerOpts) + or die "Unable to run $LEDGER_CMD @acctLedgerOpts: $!"; + + foreach my $line (<GL_TEXT_DATA>) { + print GL_TEXT_OUT $line; + } + close(GL_TEXT_DATA); die "error reading ledger output for chart of accounts: $!" unless $? == 0; + + print GL_CSV_OUT "\n\"ACCOUNT:\",\"$acct\"\n\"PERIOD START:\",\"$formattedBeginDate\"\n\"PERIOD END:\",\"$formattedEndDate\"\n"; + print GL_CSV_OUT '"DATE","CHECK NUM","NAME","TRANSACTION AMT","BALANCE"'; + + my $formatString = '"%(date)","%C","%P","%t",""'; + foreach my $tagField (qw/Receipt Invoice Statement Contract PurchaseOrder Approval Check IncomeDistributionAnalysis CurrencyRate/) { + print GL_CSV_OUT ',"', $tagField, '"'; + $formatString .= ',"link:%(tag(\'' . $tagField . '\'))"'; + } + $formatString .= "\n"; + print GL_CSV_OUT "\n"; + if ($acct =~ /^(Assets|Liabilities|Accrued|Unearned Income)/) { + $balanceData{totalBegin}{$acct} = $ZERO unless defined $balanceData{totalBegin}{$acct}; + print GL_CSV_OUT "\"$formattedBeginDate\"", ',"","BALANCE","","$', "$balanceData{totalBegin}{$acct}\"\n"; + } + + @acctLedgerOpts = ('-V', '-F', $formatString, '-w', '--sort', 'd', '-b', $beginDate, '-e', $endDate, @otherLedgerOpts, 'reg', $acct); + open(GL_CSV_DATA, "-|", $LEDGER_CMD, @acctLedgerOpts) + or die "Unable to run $LEDGER_CMD @acctLedgerOpts: $!"; + + foreach my $line (<GL_CSV_DATA>) { + $line =~ s/"link:"/""/g; + print GL_CSV_OUT $line; + next if $line =~ /ACCOUNT:.*PERIOD/; # Skip column header lines + $line =~ s/^"[^"]*","[^"]*","[^"]*","[^"]*","[^"]*",//; + while ($line =~ s/^"([^"]*)"(,|$)//) { + my $file = $1; + next if $file =~ /^\s*$/; + $file =~ s/^link:(.*)$/$1/; + warn "$file does not exist and/or is not readable" unless -r $file; + print MANIFEST "$file\n" if not defined $manifest{$file}; + $manifest{$file} = $line; + } + } + if ($acct =~ /^(Assets|Liabilities|Accrued|Unearned Income)/) { + $balanceData{totalEnd}{$acct} = $ZERO unless defined $balanceData{totalEnd}{$acct}; + print GL_CSV_OUT "\"$formattedEndDate\"", ',"","BALANCE","","$', "$balanceData{totalEnd}{$acct}\"\n"; + } + print GL_CSV_OUT "pagebreak\n"; + close(GL_CSV_DATA); die "error reading ledger output for chart of accounts: $!" unless $? == 0; +} +close(GL_TEXT_OUT); die "error writing to general-ledger.txt: $!" unless $? == 0; +close(GL_CSV_OUT); die "error writing to general-ledger.csv: $!" unless $? == 0; +############################################################################### +# +# Local variables: +# compile-command: "perl -c general-ledger-report.plx" +# End: + diff --git a/contrib/non-profit-audit-reports/ooolib2/__init__.py b/contrib/non-profit-audit-reports/ooolib2/__init__.py new file mode 100644 index 00000000..6106fc5c --- /dev/null +++ b/contrib/non-profit-audit-reports/ooolib2/__init__.py @@ -0,0 +1,1987 @@ +"ooolib-python - Copyright (C) 2006-2009 Joseph Colton" + +# ooolib-python - Python module for creating Open Document Format documents. +# Copyright (C) 2006-2009 Joseph Colton + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +# You can contact me by email at josephcolton@gmail.com + +# Import Standard Modules +import zipfile # Needed for reading/writing documents +import time +import sys +import glob +import os +import re +import xml.parsers.expat # Needed for parsing documents + +def version_number(): + "Get the ooolib-python version number" + return "0.0.17" + +def version(): + "Get the ooolib-python version" + return "ooolib-python-%s" % version_number() + +def clean_string(data): + "Returns an XML friendly copy of the data string" + + data = unicode(data) # This line thanks to Chris Ender + + data = data.replace('&', '&') + data = data.replace("'", ''') + data = data.replace('"', '"') + data = data.replace('<', '<') + data = data.replace('>', '>') + data = data.replace('\t', '<text:tab-stop/>') + data = data.replace('\n', '<text:line-break/>') + return data + +class XML: + "XML Class - Used to convert nested lists into XML" + def __init__(self): + "Initialize ooolib XML instance" + pass + + def _xmldata(self, data): + datatype = data.pop(0) + datavalue = data.pop(0) + outstring = '%s' % datavalue + return outstring + + def _xmltag(self, data): + outstring = '' + # First two + datatype = data.pop(0) + dataname = data.pop(0) + outstring = '<%s' % dataname + # Element Section + element = 1 + while(data): + # elements + newdata = data.pop(0) + if (newdata[0] == 'element' and element): + newstring = self._xmlelement(newdata) + outstring = '%s %s' % (outstring, newstring) + continue + if (newdata[0] != 'element' and element): + element = 0 + outstring = '%s>' % outstring + if (newdata[0] == 'tag' or newdata[0] == 'tagline'): + outstring = '%s\n' % outstring + if (newdata[0] == 'tag'): + newstring = self._xmltag(newdata) + outstring = '%s%s' % (outstring, newstring) + continue + if (newdata[0] == 'tagline'): + newstring = self._xmltagline(newdata) + outstring = '%s%s' % (outstring, newstring) + continue + if (newdata[0] == 'data'): + newstring = self._xmldata(newdata) + outstring = '%s%s' % (outstring, newstring) + continue + if (element): + element = 0 + outstring = '%s>\n' % outstring + outstring = '%s</%s>\n' % (outstring, dataname) + return outstring + + def _xmltagline(self, data): + outstring = '' + # First two + datatype = data.pop(0) + dataname = data.pop(0) + outstring = '<%s' % dataname + # Element Section + while(data): + # elements + newdata = data.pop(0) + if (newdata[0] != 'element'): break + newstring = self._xmlelement(newdata) + outstring = '%s %s' % (outstring, newstring) + outstring = '%s/>\n' % outstring + # Non-Element Section should not exist + return outstring + + def _xmlelement(self, data): + datatype = data.pop(0) + dataname = data.pop(0) + datavalue = data.pop(0) + outstring = '%s="%s"' % (dataname, datavalue) + return outstring + + def convert(self, data): + """Convert nested lists into XML + + The convert method takes a nested lists and converts them + into XML to be used in Open Document Format documents. + There are three types of lists that are recognized at this + time. They are as follows: + + 'tag' - Tag opens a set of data that is eventually closed + with a similar tag. + List: ['tag', 'xml'] + XML: <xml></xml> + + 'tagline' - Taglines are similar to tags, except they open + and close themselves. + List: ['tagline', 'xml'] + XML: <xml/> + + 'element' - Elements are pieces of information stored in an + opening tag or tagline. + List: ['element', 'color', 'blue'] + XML: color="blue" + + 'data' - Data is plain text directly inserted into the XML + document. + List: ['data', 'hello'] + XML: hello + + Bring them all together for something like this. + + Lists: + ['tag', 'xml', ['element', 'a', 'b'], ['tagline', 'xml2'], + ['data', 'asdf']] + + XML: + <xml a="b"><xml2/>asdf</xml> + """ + outlines = [] + outlines.append('<?xml version="1.0" encoding="UTF-8"?>') + if (type(data) == type([]) and len(data) > 0): + if data[0] == 'tag': outlines.append(self._xmltag(data)) + return outlines + +class Meta: + "Meta Data Class" + + def __init__(self, doctype, debug=False): + self.doctype = doctype + + # Set the debug mode + self.debug = debug + + # The generator should always default to the version number + self.meta_generator = version() + self.meta_title = '' + self.meta_subject = '' + self.meta_description = '' + self.meta_keywords = [] + self.meta_creator = 'ooolib-python' + self.meta_editor = '' + self.meta_user1_name = 'Info 1' + self.meta_user2_name = 'Info 2' + self.meta_user3_name = 'Info 3' + self.meta_user4_name = 'Info 4' + self.meta_user1_value = '' + self.meta_user2_value = '' + self.meta_user3_value = '' + self.meta_user4_value = '' + self.meta_creation_date = self.meta_time() + + # Parser data + self.parser_element_list = [] + self.parser_element = "" + self.parser_count = 0 + + def set_meta(self, metaname, value): + """Set meta data in your document. + + Currently implemented metaname options are as follows: + 'creator' - The document author + """ + if metaname == 'creator': self.meta_creator = value + if metaname == 'editor': self.meta_editor = value + if metaname == 'title': self.meta_title = value + if metaname == 'subject': self.meta_subject = value + if metaname == 'description': self.meta_description = value + if metaname == 'user1name': self.meta_user1_name = value + if metaname == 'user2name': self.meta_user2_name = value + if metaname == 'user3name': self.meta_user3_name = value + if metaname == 'user4name': self.meta_user4_name = value + if metaname == 'user1value': self.meta_user1_value = value + if metaname == 'user2value': self.meta_user2_value = value + if metaname == 'user3value': self.meta_user3_value = value + if metaname == 'user4value': self.meta_user4_value = value + if metaname == 'keyword': + if value not in self.meta_keywords: + self.meta_keywords.append(value) + + def get_meta_value(self, metaname): + "Get meta data value for a given metaname." + + if metaname == 'creator': return self.meta_creator + if metaname == 'editor': return self.meta_editor + if metaname == 'title': return self.meta_title + if metaname == 'subject': return self.meta_subject + if metaname == 'description': return self.meta_description + if metaname == 'user1name': return self.meta_user1_name + if metaname == 'user2name': return self.meta_user2_name + if metaname == 'user3name': return self.meta_user3_name + if metaname == 'user4name': return self.meta_user4_name + if metaname == 'user1value': return self.meta_user1_value + if metaname == 'user2value': return self.meta_user2_value + if metaname == 'user3value': return self.meta_user3_value + if metaname == 'user4value': return self.meta_user4_value + if metaname == 'keyword': return self.meta_keywords + + def meta_time(self): + "Return time string in meta data format" + t = time.localtime() + stamp = "%04d-%02d-%02dT%02d:%02d:%02d" % (t[0], t[1], t[2], t[3], t[4], t[5]) + return stamp + + def parse_start_element(self, name, attrs): + if self.debug: print '* Start element:', name + self.parser_element_list.append(name) + self.parser_element = self.parser_element_list[-1] + + # Need the meta name from the user-defined tags + if (self.parser_element == "meta:user-defined"): + self.parser_count += 1 + # Set user-defined name + self.set_meta("user%dname" % self.parser_count, attrs['meta:name']) + + # Debugging statements + if self.debug: print " List: ", self.parser_element_list + if self.debug: print " Attributes: ", attrs + + + def parse_end_element(self, name): + if self.debug: print '* End element:', name + if name != self.parser_element: + print "Tag Mismatch: '%s' != '%s'" % (name, self.parser_element) + self.parser_element_list.pop() + + # Readjust parser_element_list and parser_element + if (self.parser_element_list): + self.parser_element = self.parser_element_list[-1] + else: + self.parser_element = "" + + def parse_char_data(self, data): + if self.debug: print " Character data: ", repr(data) + + # Collect Meta data fields + if (self.parser_element == "dc:title"): + self.set_meta("title", data) + if (self.parser_element == "dc:description"): + self.set_meta("description", data) + if (self.parser_element == "dc:subject"): + self.set_meta("subject", data) + if (self.parser_element == "meta:initial-creator"): + self.set_meta("creator", data) + + # Try to maintain the same creation date + if (self.parser_element == "meta:creation-date"): + self.meta_creation_date = data + + # The user defined fields need to be kept track of, parser_count does that + if (self.parser_element == "meta:user-defined"): + self.set_meta("user%dvalue" % self.parser_count, data) + + def meta_parse(self, data): + "Parse Meta Data from a meta.xml file" + + # Debugging statements + if self.debug: + # Sometimes it helps to see the document that was read from + print data + print "\n\n\n" + + # Create parser + parser = xml.parsers.expat.ParserCreate() + # Set up parser callback functions + parser.StartElementHandler = self.parse_start_element + parser.EndElementHandler = self.parse_end_element + parser.CharacterDataHandler = self.parse_char_data + + # Actually parse the data + parser.Parse(data, 1) + + def get_meta(self): + "Generate meta.xml file data" + self.meta_date = self.meta_time() + self.data = ['tag', 'office:document-meta', + ['element', 'xmlns:office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'], + ['element', 'xmlns:xlink', 'http://www.w3.org/1999/xlink'], + ['element', 'xmlns:dc', 'http://purl.org/dc/elements/1.1/'], + ['element', 'xmlns:meta', 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0'], + ['element', 'xmlns:ooo', 'http://openoffice.org/2004/office'], + ['element', 'office:version', '1.0'], + ['tag', 'office:meta', + ['tag', 'meta:generator', # Was: 'OpenOffice.org/2.0$Linux OpenOffice.org_project/680m5$Build-9011' + ['data', self.meta_generator]], # Generator is set the the ooolib-python version. + ['tag', 'dc:title', + ['data', self.meta_title]], # This data is the document title + ['tag', 'dc:description', + ['data', self.meta_description]], # This data is the document description + ['tag', 'dc:subject', + ['data', self.meta_subject]], # This data is the document subject + ['tag', 'meta:initial-creator', + ['data', self.meta_creator]], # This data is the document creator + ['tag', 'meta:creation-date', + ['data', self.meta_creation_date]], # This is the original creation date of the document + ['tag', 'dc:creator', + ['data', self.meta_editor]], # This data is the document editor + ['tag', 'dc:date', + ['data', self.meta_date]], # This is the last modified date of the document + ['tag', 'dc:language', + ['data', 'en-US']], # We will probably always use en-US for language + ['tag', 'meta:editing-cycles', + ['data', '1']], # Edit cycles will probably always be 1 for generated documents + ['tag', 'meta:editing-duration', + ['data', 'PT0S']], # Editing duration is modified - creation date + ['tag', 'meta:user-defined', + ['element', 'meta:name', self.meta_user1_name], + ['data', self.meta_user1_value]], + ['tag', 'meta:user-defined', + ['element', 'meta:name', self.meta_user2_name], + ['data', self.meta_user2_value]], + ['tag', 'meta:user-defined', + ['element', 'meta:name', self.meta_user3_name], + ['data', self.meta_user3_value]], + ['tag', 'meta:user-defined', + ['element', 'meta:name', self.meta_user4_name], + ['data', self.meta_user4_value]]]] +# ['tagline', 'meta:document-statistic', +# ['element', 'meta:table-count', len(self.sheets)], # len(self.sheets) ? +# ['element', 'meta:cell-count', '15']]]] # Not sure how to keep track + + # Generate content.xml XML data + xml = XML() + self.lines = xml.convert(self.data) + self.filedata = '\n'.join(self.lines) + # Return generated data + return self.filedata + + + +class CalcStyles: + "Calc Style Management - Used to keep track of created styles." + + def __init__(self): + self.style_config = {} + # Style Counters + self.style_table = 1 + self.style_column = 1 + self.style_row = 1 + self.style_cell = 1 + # Style Properties (Defaults) - To be used later + self.property_column_width_default = '0.8925in' # Default Column Width + self.property_row_height_default = '0.189in' # Default Row Height + # Set Defaults + self.property_column_width = '0.8925in' # Default Column Width + self.property_row_height = '0.189in' # Default Row Height + self.property_cell_bold = False # Bold off be default + self.property_cell_italic = False # Italic off be default + self.property_cell_underline = False # Underline off be default + self.property_cell_fg_color = 'default' # Text Color Default + self.property_cell_bg_color = 'default' # Cell Background Default + self.property_cell_bg_image = 'none' # Cell Background Default + self.property_cell_fontsize = '10' # Cell Font Size Default + self.property_cell_valign = 'default' # Vertial Alignment Default + self.property_cell_halign = 'default' # Horizantal Alignment Default + + def get_next_style(self, style): + "Returns the next style code for the given style" + style_code = "" + if style == 'table': + style_code = 'ta%d' % self.style_table + self.style_table+=1 + if style == 'column': + style_code = 'co%d' % self.style_column + self.style_column+=1 + if style == 'row': + style_code = 'ro%d' % self.style_row + self.style_row+=1 + if style == 'cell': + style_code = 'ce%d' % self.style_cell + self.style_cell+=1 + return style_code + + def set_property(self, style, name, value): + "Sets a property which will later be turned into a code" + if style == 'table': + pass + if style == 'column': + if name == 'style:column-width': self.property_column_width = value + if style == 'row': + if name == 'style:row-height': self.property_row_height = value + if style == 'cell': + if name == 'bold' and type(value) == type(True): self.property_cell_bold = value + if name == 'italic' and type(value) == type(True): self.property_cell_italic = value + if name == 'underline' and type(value) == type(True): self.property_cell_underline = value + if name == 'fontsize': self.property_cell_fontsize = value + if name == 'color': + self.property_cell_fg_color = 'default' + redata = re.search("^(#[\da-fA-F]{6})$", value) + if redata: self.property_cell_fg_color = value.lower() + if name == 'background': + self.property_cell_bg_color = 'default' + redata = re.search("^(#[\da-fA-F]{6})$", value) + if redata: self.property_cell_bg_color = value.lower() + if name == 'backgroundimage': + self.property_cell_bg_image = value + if name == 'valign': + self.property_cell_valign = value + if name == 'halign': + self.property_cell_halign = value + + def get_style_code(self, style): + style_code = "" + if style == 'table': + style_code = "ta1" + if style == 'column': + style_data = tuple([style, + ('style:column-width', self.property_column_width)]) + if style_data in self.style_config: + # Style Exists, return code + style_code = self.style_config[style_data] + else: + # Style does not exist, create code and return it + style_code = self.get_next_style(style) + self.style_config[style_data] = style_code + if style == 'row': + style_data = tuple([style, + ('style:row-height', self.property_row_height)]) + if style_data in self.style_config: + # Style Exists, return code + style_code = self.style_config[style_data] + else: + # Style does not exist, create code and return it + style_code = self.get_next_style(style) + self.style_config[style_data] = style_code + if style == 'cell': + style_data = [style] + # Add additional styles + if self.property_cell_bold: style_data.append(('bold', True)) + if self.property_cell_italic: style_data.append(('italic', True)) + if self.property_cell_underline: style_data.append(('underline', True)) + if self.property_cell_fontsize != '10': + style_data.append(('fontsize', self.property_cell_fontsize)) + if self.property_cell_fg_color != 'default': + style_data.append(('color', self.property_cell_fg_color)) + if self.property_cell_bg_color != 'default': + style_data.append(('background', self.property_cell_bg_color)) + if self.property_cell_bg_image != 'none': + style_data.append(('backgroundimage', self.property_cell_bg_image)) + if self.property_cell_valign != 'default': + style_data.append(('valign', self.property_cell_valign)) + if self.property_cell_halign != 'default': + style_data.append(('halign', self.property_cell_halign)) + + style_data = tuple(style_data) + if style_data in self.style_config: + # Style Exists, return code + style_code = self.style_config[style_data] + else: + # Style does not exist, create code and return it + style_code = self.get_next_style(style) + self.style_config[style_data] = style_code + return style_code + + def get_automatic_styles(self): + "Return 'office:automatic-styles' lists" + automatic_styles = ['tag', 'office:automatic-styles'] + + for style_data in self.style_config: + style_code = self.style_config[style_data] + style_data = list(style_data) + style = style_data.pop(0) + + if style == 'column': + style_list = ['tag', 'style:style', + ['element', 'style:name', style_code], # Column 'co1' properties + ['element', 'style:family', 'table-column']] + tagline = ['tagline', 'style:table-column-properties', + ['element', 'fo:break-before', 'auto']] # unsure what break before means + + for set in style_data: + name, value = set + if name == 'style:column-width': + tagline.append(['element', 'style:column-width', value]) + style_list.append(tagline) + automatic_styles.append(style_list) + + if style == 'row': + style_list = ['tag', 'style:style', + ['element', 'style:name', style_code], # Column 'ro1' properties + ['element', 'style:family', 'table-row']] + tagline = ['tagline', 'style:table-row-properties'] + + for set in style_data: + name, value = set + if name == 'style:row-height': + tagline.append(['element', 'style:row-height', value]) + tagline.append(['element', 'fo:break-before', 'auto']) +# tagline.append(['element', 'style:use-optimal-row-height', 'true']) # Overrides settings + style_list.append(tagline) + automatic_styles.append(style_list) + + if style == 'pagebreak': + style_list = ['tag', 'style:style', + ['element', 'style:name', style_code], # Column 'ro1' properties + ['element', 'style:family', 'table-row']] + tagline = ['tagline', 'style:table-row-properties'] + + for set in style_data: + name, value = set + if name == 'style:row-height': + tagline.append(['element', 'style:row-height', value]) + tagline.append(['element', 'fo:break-before', 'page']) +# tagline.append(['element', 'style:use-optimal-row-height', 'true']) # Overrides settings + style_list.append(tagline) + automatic_styles.append(style_list) + + if style == 'cell': + style_list = ['tag', 'style:style', + ['element', 'style:name', style_code], # ce1 style + ['element', 'style:family', 'table-cell'], # cell + ['element', 'style:parent-style-name', 'Default']] # parent is Default + # hack for currency + if style_code == 'ce1': + style_list.append(['element', + 'style:data-style-name', + 'N104']) + + # Cell Properties + tagline = ['tag', 'style:table-cell-properties'] + tagline_additional = [] + for set in style_data: + name, value = set + if name == 'background': + tagline.append(['element', 'fo:background-color', value]) + if name == 'backgroundimage': + tagline.append(['element', 'fo:background-color', 'transparent']) + # Additional tags added later + bgimagetag = ['tagline', 'style:background-image'] + bgimagetag.append(['element', 'xlink:href', value]) + bgimagetag.append(['element', 'xlink:type', 'simple']) + bgimagetag.append(['element', 'xlink:actuate', 'onLoad']) + tagline_additional.append(bgimagetag) + if name == 'valign': + if value in ['top', 'bottom', 'middle']: + tagline.append(['element', 'style:vertical-align', value]) + if name == 'halign': + tagline.append(['element', 'style:text-align-source', 'fix']) + if value in ['filled']: + tagline.append(['element', 'style:repeat-content', 'true']) + else: + tagline.append(['element', 'style:repeat-content', 'false']) + + # Add any additional internal tags + while tagline_additional: + tagadd = tagline_additional.pop(0) + tagline.append(tagadd) + + style_list.append(tagline) + + # Paragraph Properties + tagline = ['tagline', 'style:paragraph-properties'] + tagline_valid = False + for set in style_data: + name, value = set + if name == 'halign': + tagline_valid = True + if value in ['center']: + tagline.append(['element', 'fo:text-align', 'center']) + if value in ['end', 'right']: + tagline.append(['element', 'fo:text-align', 'end']) + if value in ['start', 'filled', 'left']: + tagline.append(['element', 'fo:text-align', 'start']) + if value in ['justify']: + tagline.append(['element', 'fo:text-align', 'justify']) + # Conditionally add the tagline + if tagline_valid: style_list.append(tagline) + + + # Text Properties + tagline = ['tagline', 'style:text-properties'] + for set in style_data: + name, value = set + if name == 'bold': + tagline.append(['element', 'fo:font-weight', 'bold']) + if name == 'italic': + tagline.append(['element', 'fo:font-style', 'italic']) + if name == 'underline': + tagline.append(['element', 'style:text-underline-style', 'solid']) + tagline.append(['element', 'style:text-underline-width', 'auto']) + tagline.append(['element', 'style:text-underline-color', 'font-color']) + if name == 'color': + tagline.append(['element', 'fo:color', value]) + if name == 'fontsize': + tagline.append(['element', 'fo:font-size', '%spt' % value]) + style_list.append(tagline) + + automatic_styles.append(style_list) + + + # Attach ta1 style + automatic_styles.append(['tag', 'style:style', + ['element', 'style:name', 'ta1'], + ['element', 'style:family', 'table'], + ['element', 'style:master-page-name', 'Default'], + ['tagline', 'style:table-properties', + ['element', 'table:display', 'true'], + ['element', 'style:writing-mode', 'lr-tb']]]) + + + return automatic_styles + + + +class CalcSheet: + "Calc Sheet Class - Used to keep track of the data for an individual sheet." + + def __init__(self, sheetname): + "Initialize a sheet" + self.sheet_name = sheetname + self.sheet_values = {} + self.sheet_config = {} + self.max_col = 0 + self.max_row = 0 + + def get_sheet_dimensions(self): + "Returns the max column and row" + return (self.max_col, self.max_row) + + def clean_formula(self, data): + "Returns a formula for use in ODF" + # Example Translations + # '=SUM(A1:A2)' + # datavalue = 'oooc:=SUM([.A1:.A2])' + # '=IF((A5>A4);A4;"")' + # datavalue = 'oooc:=IF(([.A5]>[.A4]);[.A4];"")' + data = str(data) + data = clean_string(data) + redata = re.search('^=([A-Z]+)(\(.*)$', data) + if redata: + # funct is the function name. The rest if the string will be the functArgs + funct = redata.group(1) + functArgs = redata.group(2) + # Search for cell lebels and replace them + reList = re.findall('([A-Z]+\d+)', functArgs) + # sort and keep track so we do not do a cell more than once + reList.sort() + lastVar = '' + while reList: + # Replace each cell label + curVar = reList.pop() + if curVar == lastVar: continue + lastVar = curVar + functArgs = functArgs.replace(curVar, '[.%s]' % curVar) + data = 'oooc:=%s%s' % (funct, functArgs) + return data + + def get_name(self): + "Returns the sheet name" + return self.sheet_name + + def set_name(self, sheetname): + "Resets the sheet name" + self.sheet_name = sheetname + + def get_sheet_values(self): + "Returns the sheet cell values" + return self.sheet_values + + def get_sheet_value(self, col, row): + "Get the value contents of a cell" + cell = (col, row) + if cell in self.sheet_values: + return self.sheet_values[cell] + else: + return None + + def get_sheet_config(self): + "Returns the sheet cell properties" + return self.sheet_config + + def set_sheet_config(self, location, style_code): + "Sets Style Code for a given location" + self.sheet_config[location] = style_code + + def set_sheet_value(self, cell, datatype, datavalue): + """Sets the value for a specific cell + + cell must be in the format (col, row) where row and col are int. + Example: B5 would be written as (2, 5) + datatype must be one of 'string', 'float', 'formula', 'currency' + datavalue should be a string + """ + # Catch invalid data + if type(cell) != type(()) or len(cell) != 2: + print "Invalid Cell" + return + (col, row) = cell + if type(col) != type(1): + print "Invalid Cell" + return + if type(row) != type(1): + print "Invalid Cell" + return + # Fix String Data + if datatype in ['string', 'annotation']: + datavalue = clean_string(datavalue) + # Fix Link Data. Link's value is a tuple containing (url, description) + if (datatype == 'link'): + url = clean_string(datavalue[0]) + desc = clean_string(datavalue[1]) + datavalue = (url, desc) + # Fix Formula Data + if datatype == 'formula': + datavalue = self.clean_formula(datavalue) + # Adjust maximum sizes + if col > self.max_col: self.max_col = col + if row > self.max_row: self.max_row = row + datatype = str(datatype) + if (datatype not in ['string', 'float', 'currency', 'formula', 'annotation', 'link']): + # Set all unknown cell types to string + datatype = 'string' + datavalue = str(datavalue) + + # The following lines are taken directly from HPS + # self.sheet_values[cell] = (datatype, datavalue) + # HPS: Cell content is now a list of tuples instead of a tuple + # While storing here, store the cell contents first and the annotation next. While generating the XML reverse this + contents = self.sheet_values.get(cell, {'annotation':None,'link':None, 'value':None}) + if datatype == 'annotation': + contents['annotation'] = (datatype, datavalue) + elif datatype == 'link': + contents['link'] = (datatype, datavalue) + else: + contents['value'] = (datatype, datavalue) + + self.sheet_values[cell] = contents + + + def get_lists(self): + "Returns nested lists for XML processing" + if (self.max_col == 0 and self.max_row == 0): + sheet_lists = ['tag', 'table:table', + ['element', 'table:name', self.sheet_name], # Set the Sheet Name + ['element', 'table:style-name', 'ta1'], + ['element', 'table:print', 'false'], + ['tagline', 'table:table-column', + ['element', 'table:style-name', 'co1'], + ['element', 'table:default-cell-style-name', 'Default']], + ['tag', 'table:table-row', + ['element', 'table:style-name', 'ro1'], + ['tagline', 'table:table-cell']]] + else: + # Base Information + sheet_lists = ['tag', 'table:table', + ['element', 'table:name', self.sheet_name], # Set the sheet name + ['element', 'table:style-name', 'ta1'], + ['element', 'table:print', 'false']] + +# ['tagline', 'table:table-column', +# ['element', 'table:style-name', 'co1'], +# ['element', 'table:number-columns-repeated', self.max_col], # max_col? '2' +# ['element', 'table:default-cell-style-name', 'Default']], + + # Need to add column information + for col in range(1, self.max_col+1): + location = ('col', col) + style_code = 'co1' + if location in self.sheet_config: + style_code = self.sheet_config[location] + sheet_lists.append(['tagline', 'table:table-column', + ['element', 'table:style-name', style_code], + ['element', 'table:default-cell-style-name', 'Default']]) + + + # Need to create each row + for row in range(1, self.max_row + 1): + location = ('row', row) + style_code = 'ro1' + if location in self.sheet_config: + style_code = self.sheet_config[location] + rowlist = ['tag', 'table:table-row', + ['element', 'table:style-name', style_code]] + for col in range(1, self.max_col + 1): + cell = (col, row) + style_code = 'ce1' # Default all cells to ce1 + if cell in self.sheet_config: + style_code = self.sheet_config[cell] # Lookup cell if available + if cell in self.sheet_values: + # (datatype, datavalue) = self.sheet_values[cell] # Marked for removal + collist = ['tag', 'table:table-cell'] + if style_code != 'ce1': + collist.append(['element', 'table:style-name', style_code]) + + # Contents, annotations, and links added by HPS + contents = self.sheet_values[cell] # cell contents is a dictionary + if contents['value']: + (datatype, datavalue) = contents['value'] + if datatype == 'float': + collist.append(['element', 'office:value-type', datatype]) + collist.append(['element', 'office:value', datavalue]) + if datatype == 'currency': + collist.append(['element', 'table:style-name', "ce1"]) + collist.append(['element', 'office:value-type', datatype]) + collist.append(['element', 'office:currency', 'USD']) + collist.append(['element', 'office:value', datavalue]) + + if datatype == 'string': + collist.append(['element', 'office:value-type', datatype]) + if datatype == 'formula': + collist.append(['element', 'table:formula', datavalue]) + collist.append(['element', 'office:value-type', 'float']) + collist.append(['element', 'office:value', '0']) + datavalue = '0' + else: + datavalue = None + + if contents['annotation']: + (annotype, annoval) = contents['annotation'] + collist.append(['tag', 'office:annotation', + ['tag', 'text:p', ['data', annoval]]]) + + if contents['link']: + (linktype, linkval) = contents['link'] + if datavalue: + collist.append(['tag', 'text:p', ['data', datavalue], + ['tag', 'text:a', ['element', 'xlink:href', linkval[0]], + ['data', linkval[1]]]]) + else: # no value; just fill the link + collist.append(['tag', 'text:p', + ['tag', 'text:a', ['element', 'xlink:href', linkval[0]], + ['data', linkval[1]]]]) + else: + if datavalue: + collist.append(['tag', 'text:p', ['data', datavalue]]) + + + + else: + collist = ['tagline', 'table:table-cell'] + rowlist.append(collist) + sheet_lists.append(rowlist) + return sheet_lists + +class Calc: + "Calc Class - Used to create OpenDocument Format Calc Spreadsheets." + def __init__(self, sheetname=None, opendoc=None, debug=False): + "Initialize ooolib Calc instance" + # Default to no debugging + self.debug = debug + if not sheetname: sheetname = "Sheet1" + self.sheets = [CalcSheet(sheetname)] # The main sheet will be initially called 'Sheet1' + self.sheet_index = 0 # We initially start on the first sheet + self.styles = CalcStyles() + self.meta = Meta('ods') + self.styles.get_style_code('column') # Force generation of default column + self.styles.get_style_code('row') # Force generation of default row + self.styles.get_style_code('table') # Force generation of default table + self.styles.get_style_code('cell') # Force generation of default cell + self.manifest_files = [] # List of extra files included + self.manifest_index = 1 # Index of added manifest files + + # Data Parsing + self.parser_element_list = [] + self.parser_element = "" + self.parser_sheet_num = 0 + self.parser_sheet_row = 0 + self.parser_sheet_column = 0 + self.parser_cell_repeats = 0 + self.parser_cell_string_pending = False + self.parser_cell_string_line = "" + + # See if we need to read a document + if opendoc: + # Verify that the document exists + if self.debug: print "Opening Document: %s" % opendoc + + # Okay, now we load the file + self.load(opendoc) + + def debug_level(self, level): + """Set debug level: + True if you want debugging messages + False if you do not. + """ + self.debug = level + + def file_mimetype(self, filename): + "Determine the filetype from the filename" + parts = filename.lower().split('.') + ext = parts[-1] + if (ext == 'png'): return (ext, "image/png") + if (ext == 'gif'): return (ext, "image/gif") + return (ext, "image/unknown") + + def add_file(self, filename): + """Prepare a file for loading into ooolib + + The filename should be the local filesystem name for + the file. The file is then prepared to be included in + the creation of the final document. The file needs to + remain in place so that it is available when the actual + document creation happens. + """ + # mimetype set to (ext, filetype) + mimetype = self.file_mimetype(filename) + newname = "Pictures/%08d.%s" % (self.manifest_index, mimetype[0]) + self.manifest_index += 1 + filetype = mimetype[1] + self.manifest_files.append((filename, filetype, newname)) + return newname + + def set_meta(self, metaname, value): + "Set meta data in your document." + self.meta.set_meta(metaname, value) + + def get_meta_value(self, metaname): + "Get meta data value for a given metaname" + return self.meta.get_meta_value(metaname) + + def get_sheet_name(self): + "Returns the sheet name" + return self.sheets[self.sheet_index].get_name() + + def get_sheet_dimensions(self): + "Returns the sheet dimensions in (cols, rows)" + return self.sheets[self.sheet_index].get_sheet_dimensions() + + def set_column_property(self, column, name, value): + "Set Column Properties" + if name == 'width': + # column number column needs column-width set to value + self.styles.set_property('column', 'style:column-width', value) + style_code = self.styles.get_style_code('column') + self.sheets[self.sheet_index].set_sheet_config(('col', column), style_code) + + def set_row_property(self, row, name, value): + "Set row Properties" + if name == 'height': + # row number row needs row-height set to value + self.styles.set_property('row', 'style:row-height', value) + style_code = self.styles.get_style_code('row') + self.sheets[self.sheet_index].set_sheet_config(('row', row), style_code) + + def set_cell_property(self, name, value): + """Turn and off cell properties + + Actual application of properties is handled by setting a value.""" + # background images need to be handled a little differently + # because they need to also be inserted into the final document + if (name == 'backgroundimage'): + # Add file and modify value + value = self.add_file(value) + self.styles.set_property('cell', name, value) + + def get_sheet_index(self): + "Return the current sheet index number" + return self.sheet_index + + def set_sheet_index(self, index): + "Set the sheet index" + if type(index) == type(1): + if index >= 0 and index < len(self.sheets): + self.sheet_index = index + return self.sheet_index + + def get_sheet_count(self): + "Returns the number of existing sheets" + return len(self.sheets) + + def new_sheet(self, sheetname): + "Create a new sheet" + self.sheet_index = len(self.sheets) + self.sheets.append(CalcSheet(sheetname)) + return self.sheet_index + + def set_cell_value(self, col, row, datatype, value): + "Set the value for a given cell" + self.sheets[self.sheet_index].set_sheet_value((col, row), datatype, value) + style_code = self.styles.get_style_code('cell') + self.sheets[self.sheet_index].set_sheet_config((col, row), style_code) + + def get_cell_value(self, col, row): + "Get a cell value tuple (type, value) for a given cell" + sheetvalue = self.sheets[self.sheet_index].get_sheet_value(col, row) + # We stop here if there is no value for sheetvalue + if sheetvalue == None: return sheetvalue + # Now check to see if we have a value tuple + if 'value' in sheetvalue: + return sheetvalue['value'] + else: + return None + + def load(self, filename): + """Load .ods spreadsheet. + + The load function loads data from a document into the current cells. + """ + # Read in the important files + + # meta.xml + data = self._zip_read(filename, "meta.xml") + self.meta.meta_parse(data) + + # content.xml + data = self._zip_read(filename, "content.xml") + self.content_parse(data) + + # settings.xml - I do not remember putting anything here + # styles.xml - I do not remember putting anything here + + def parse_content_start_element(self, name, attrs): + if self.debug: print '* Start element:', name + self.parser_element_list.append(name) + self.parser_element = self.parser_element_list[-1] + + # Keep track of the current sheet number + if (self.parser_element == 'table:table'): + # Move to starting cell + self.parser_sheet_row = 0 + self.parser_sheet_column = 0 + # Increment the sheet number count + self.parser_sheet_num += 1 + if (self.parser_sheet_num - 1 != self.sheet_index): + # We are not on the first sheet and need to create a new sheet. + # We will automatically move to the new sheet + sheetname = "Sheet%d" % self.parser_sheet_num + if 'table:name' in attrs: sheetname = attrs['table:name'] + self.new_sheet(sheetname) + else: + # We are on the first sheet and will need to overwrite the default name + sheetname = "Sheet%d" % self.parser_sheet_num + if 'table:name' in attrs: sheetname = attrs['table:name'] + self.sheets[self.sheet_index].set_name(sheetname) + + # Update the row numbers + if (self.parser_element == 'table:table-row'): + self.parser_sheet_row += 1 + self.parser_sheet_column = 0 + + # Okay, now keep track of the sheet cell data + if (self.parser_element == 'table:table-cell'): + # By default it will repeat zero times + self.parser_cell_repeats = 0 + # We must be in a new column + self.parser_sheet_column += 1 + # Set some default values + datatype = "" + value = "" + # Get values from attrs hash + if 'office:value-type' in attrs: datatype = attrs['office:value-type'] + if 'office:value' in attrs: value = attrs['office:value'] + if 'table:formula' in attrs: + datatype = 'formula' + value = attrs['table:formula'] + if datatype == 'string': + datatype = "" + self.parser_cell_string_pending = True + self.parser_cell_string_line = "" + if 'table:number-columns-repeated' in attrs: + self.parser_cell_repeats = int(attrs['table:number-columns-repeated']) - 1 + # Set the cell value + if datatype: + # I should do this once per cell repeat above 0 + for i in range(0, self.parser_cell_repeats+1): + self.set_cell_value(self.parser_sheet_column+i, self.parser_sheet_row, datatype, value) + + # There are lots of interesting cases with table:table-cell data. One problem is + # reading the number of embedded spaces correctly. This code should help us get + # the number of spaces out. + + if (self.parser_element == 'text:s'): + # This means we have a number of spaces + count_num = 0 + if 'text:c' in attrs: + count_alpha = attrs['text:c'] + if (count_alpha.isdigit()): + count_num = int(count_alpha) + # I am not sure what to do if we do not have a string pending + if (self.parser_cell_string_pending == True): + # Append the currect number of spaces to the end + self.parser_cell_string_line = "%s%s" % (self.parser_cell_string_line, ' '*count_num) + + if (self.parser_element == 'text:tab-stop'): + if (self.parser_cell_string_pending == True): + self.parser_cell_string_line = "%s\t" % (self.parser_cell_string_line) + + if (self.parser_element == 'text:line-break'): + if (self.parser_cell_string_pending == True): + self.parser_cell_string_line = "%s\n" % (self.parser_cell_string_line) + + # Debugging statements + if self.debug: print " List: ", self.parser_element_list + if self.debug: print " Attributes: ", attrs + + + def parse_content_end_element(self, name): + if self.debug: print '* End element:', name + if name != self.parser_element: + print "Tag Mismatch: '%s' != '%s'" % (name, self.parser_element) + self.parser_element_list.pop() + + # If the element was text:p and we are in string mode + if (self.parser_element == 'text:p'): + if (self.parser_cell_string_pending): + self.parser_cell_string_pending = False + + # Take care of repeated cells + if (self.parser_element == 'table:table-cell'): + self.parser_sheet_column += self.parser_cell_repeats + + # Readjust parser_element_list and parser_element + if (self.parser_element_list): + self.parser_element = self.parser_element_list[-1] + else: + self.parser_element = "" + + def parse_content_char_data(self, data): + if self.debug: print " Character data: ", repr(data) + + if (self.parser_element == 'text:p' or self.parser_element == 'text:span'): + if (self.parser_cell_string_pending): + # Set the string and leave string pending mode + # This does feel a little kludgy, but it does the job + self.parser_cell_string_line = "%s%s" % (self.parser_cell_string_line, data) + + # I should do this once per cell repeat above 0 + for i in range(0, self.parser_cell_repeats+1): + self.set_cell_value(self.parser_sheet_column+i, self.parser_sheet_row, + 'string', self.parser_cell_string_line) + + + def content_parse(self, data): + "Parse Content Data from a content.xml file" + + # Debugging statements + if self.debug: + # Sometimes it helps to see the document that was read from + print data + print "\n\n\n" + + # Create parser + parser = xml.parsers.expat.ParserCreate() + # Set up parser callback functions + parser.StartElementHandler = self.parse_content_start_element + parser.EndElementHandler = self.parse_content_end_element + parser.CharacterDataHandler = self.parse_content_char_data + + # Actually parse the data + parser.Parse(data, 1) + + def save(self, filename): + """Save .ods spreadsheet. + + The save function saves the current cells and settings into a document. + """ + if self.debug: print "Writing %s" % filename + self.savefile = zipfile.ZipFile(filename, "w") + if self.debug: print " meta.xml" + self._zip_insert(self.savefile, "meta.xml", self.meta.get_meta()) + if self.debug: print " mimetype" + self._zip_insert(self.savefile, "mimetype", "application/vnd.oasis.opendocument.spreadsheet") + if self.debug: print " Configurations2/accelerator/current.xml" + self._zip_insert(self.savefile, "Configurations2/accelerator/current.xml", "") + if self.debug: print " META-INF/manifest.xml" + self._zip_insert(self.savefile, "META-INF/manifest.xml", self._ods_manifest()) + if self.debug: print " content.xml" + self._zip_insert(self.savefile, "content.xml", self._ods_content()) + if self.debug: print " settings.xml" + self._zip_insert(self.savefile, "settings.xml", self._ods_settings()) + if self.debug: print " styles.xml" + self._zip_insert(self.savefile, "styles.xml", self._ods_styles()) + + # Add additional files if needed + for fileset in self.manifest_files: + (filename, filetype, newname) = fileset + # Read in the file + data = self._file_load(filename) + if self.debug: print " Inserting '%s' as '%s'" % (filename, newname) + self._zip_insert_binary(self.savefile, newname, data) + + def _file_load(self, filename): + "Load a file" + file = open(filename, "rb") + data = file.read() + file.close() + return data + + def _zip_insert_binary(self, file, filename, data): + "Insert a binary file into the zip archive" + now = time.localtime(time.time())[:6] + info = zipfile.ZipInfo(filename) + info.date_time = now + info.compress_type = zipfile.ZIP_DEFLATED + file.writestr(info, data) + + + def _zip_insert(self, file, filename, data): + "Insert a file into the zip archive" + + # zip seems to struggle with non-ascii characters + data = data.encode('utf-8') + + now = time.localtime(time.time())[:6] + info = zipfile.ZipInfo(filename) + info.date_time = now + info.compress_type = zipfile.ZIP_DEFLATED + file.writestr(info, data) + + def _zip_read(self, file, filename): + "Get the data from a file in the zip archive by filename" + file = zipfile.ZipFile(file, "r") + data = file.read(filename) + # Need to close the file + file.close() + return data + + def _ods_content(self): + "Generate ods content.xml data" + + # This will list all of the sheets in the document + self.sheetdata = ['tag', 'office:spreadsheet'] + for sheet in self.sheets: + if self.debug: + sheet_name = sheet.get_name() + print " Creating Sheet '%s'" % sheet_name + sheet_list = sheet.get_lists() + self.sheetdata.append(sheet_list) + # Automatic Styles + self.automatic_styles = self.styles.get_automatic_styles() + + self.data = ['tag', 'office:document-content', + ['element', 'xmlns:office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'], + ['element', 'xmlns:style', 'urn:oasis:names:tc:opendocument:xmlns:style:1.0'], + ['element', 'xmlns:text', 'urn:oasis:names:tc:opendocument:xmlns:text:1.0'], + ['element', 'xmlns:table', 'urn:oasis:names:tc:opendocument:xmlns:table:1.0'], + ['element', 'xmlns:draw', 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0'], + ['element', 'xmlns:fo', 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0'], + ['element', 'xmlns:xlink', 'http://www.w3.org/1999/xlink'], + ['element', 'xmlns:dc', 'http://purl.org/dc/elements/1.1/'], + ['element', 'xmlns:meta', 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0'], + ['element', 'xmlns:number', 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0'], + ['element', 'xmlns:svg', 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0'], + ['element', 'xmlns:chart', 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0'], + ['element', 'xmlns:dr3d', 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0'], + ['element', 'xmlns:math', 'http://www.w3.org/1998/Math/MathML'], + ['element', 'xmlns:form', 'urn:oasis:names:tc:opendocument:xmlns:form:1.0'], + ['element', 'xmlns:script', 'urn:oasis:names:tc:opendocument:xmlns:script:1.0'], + ['element', 'xmlns:ooo', 'http://openoffice.org/2004/office'], + ['element', 'xmlns:ooow', 'http://openoffice.org/2004/writer'], + ['element', 'xmlns:oooc', 'http://openoffice.org/2004/calc'], + ['element', 'xmlns:dom', 'http://www.w3.org/2001/xml-events'], + ['element', 'xmlns:xforms', 'http://www.w3.org/2002/xforms'], + ['element', 'xmlns:xsd', 'http://www.w3.org/2001/XMLSchema'], + ['element', 'xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'], + ['element', 'office:version', '1.0'], + ['tagline', 'office:scripts'], + ['tag', 'office:font-face-decls', + ['tagline', 'style:font-face', + ['element', 'style:name', 'DejaVu Sans'], + ['element', 'svg:font-family', ''DejaVu Sans''], + ['element', 'style:font-pitch', 'variable']], + ['tagline', 'style:font-face', + ['element', 'style:name', 'Nimbus Sans L'], + ['element', 'svg:font-family', ''Nimbus Sans L''], + ['element', 'style:font-family-generic', 'swiss'], + ['element', 'style:font-pitch', 'variable']]], + + # Automatic Styles + self.automatic_styles, + + ['tag', 'office:body', + self.sheetdata]] # Sheets are generated from the CalcSheet class + + # Generate content.xml XML data + xml = XML() + self.lines = xml.convert(self.data) + self.filedata = '\n'.join(self.lines) + # Return generated data + return self.filedata + + def _ods_manifest(self): + "Generate ods manifest.xml data" + self.data = ['tag', 'manifest:manifest', + ['element', 'xmlns:manifest', 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0'], + ['tagline', 'manifest:file-entry', + ['element', 'manifest:media-type', 'application/vnd.oasis.opendocument.spreadsheet'], + ['element', 'manifest:full-path', '/']], + ['tagline', 'manifest:file-entry', + ['element', 'manifest:media-type', 'application/vnd.sun.xml.ui.configuration'], + ['element', 'manifest:full-path', 'Configurations2/']], + ['tagline', 'manifest:file-entry', + ['element', 'manifest:media-type', ''], + ['element', 'manifest:full-path', 'Configurations2/accelerator/']], + ['tagline', 'manifest:file-entry', + ['element', 'manifest:media-type', ''], + ['element', 'manifest:full-path', 'Configurations2/accelerator/current.xml']], + ['tagline', 'manifest:file-entry', + ['element', 'manifest:media-type', 'text/xml'], + ['element', 'manifest:full-path', 'content.xml']], + ['tagline', 'manifest:file-entry', + ['element', 'manifest:media-type', 'text/xml'], + ['element', 'manifest:full-path', 'styles.xml']], + ['tagline', 'manifest:file-entry', + ['element', 'manifest:media-type', 'text/xml'], + ['element', 'manifest:full-path', 'meta.xml']], + ['tagline', 'manifest:file-entry', + ['element', 'manifest:media-type', 'text/xml'], + ['element', 'manifest:full-path', 'settings.xml']]] + + # Add additional files to manifest list + for fileset in self.manifest_files: + (filename, filetype, newname) = fileset + addfile = ['tagline', 'manifest:file-entry', + ['element', 'manifest:media-type', filetype], + ['element', 'manifest:full-path', newname]] + self.data.append(addfile) + + # Generate content.xml XML data + xml = XML() + self.lines = xml.convert(self.data) + self.filedata = '\n'.join(self.lines) + # Return generated data + return self.filedata + + + def _ods_settings(self): + "Generate ods settings.xml data" + self.data = ['tag', 'office:document-settings', + ['element', 'xmlns:office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'], + ['element', 'xmlns:xlink', 'http://www.w3.org/1999/xlink'], + ['element', 'xmlns:config', 'urn:oasis:names:tc:opendocument:xmlns:config:1.0'], + ['element', 'xmlns:ooo', 'http://openoffice.org/2004/office'], + ['element', 'office:version', '1.0'], + ['tag', 'office:settings', + ['tag', 'config:config-item-set', + ['element', 'config:name', 'ooo:view-settings'], + ['tag', 'config:config-item', + ['element', 'config:name', 'VisibleAreaTop'], + ['element', 'config:type', 'int'], + ['data', '0']], + ['tag', 'config:config-item', + ['element', 'config:name', 'VisibleAreaLeft'], + ['element', 'config:type', 'int'], + ['data', '0']], + ['tag', 'config:config-item', + ['element', 'config:name', 'VisibleAreaWidth'], + ['element', 'config:type', 'int'], + ['data', '6774']], + ['tag', 'config:config-item', + ['element', 'config:name', 'VisibleAreaHeight'], + ['element', 'config:type', 'int'], + ['data', '2389']], + ['tag', 'config:config-item-map-indexed', + ['element', 'config:name', 'Views'], + ['tag', 'config:config-item-map-entry', + ['tag', 'config:config-item', + ['element', 'config:name', 'ViewId'], + ['element', 'config:type', 'string'], + ['data', 'View1']], + ['tag', 'config:config-item-map-named', + ['element', 'config:name', 'Tables'], + ['tag', 'config:config-item-map-entry', + ['element', 'config:name', 'Sheet1'], + ['tag', 'config:config-item', + ['element', 'config:name', 'CursorPositionX'], # Cursor Position A + ['element', 'config:type', 'int'], + ['data', '0']], + ['tag', 'config:config-item', + ['element', 'config:name', 'CursorPositionY'], # Cursor Position 1 + ['element', 'config:type', 'int'], + ['data', '0']], + ['tag', 'config:config-item', + ['element', 'config:name', 'HorizontalSplitMode'], + ['element', 'config:type', 'short'], + ['data', '0']], + ['tag', 'config:config-item', + ['element', 'config:name', 'VerticalSplitMode'], + ['element', 'config:type', 'short'], + ['data', '0']], + ['tag', 'config:config-item', + ['element', 'config:name', 'HorizontalSplitPosition'], + ['element', 'config:type', 'int'], + ['data', '0']], + ['tag', 'config:config-item', + ['element', 'config:name', 'VerticalSplitPosition'], + ['element', 'config:type', 'int'], + ['data', '0']], + ['tag', 'config:config-item', + ['element', 'config:name', 'ActiveSplitRange'], + ['element', 'config:type', 'short'], + ['data', '2']], + ['tag', 'config:config-item', + ['element', 'config:name', 'PositionLeft'], + ['element', 'config:type', 'int'], + ['data', '0']], + ['tag', 'config:config-item', + ['element', 'config:name', 'PositionRight'], + ['element', 'config:type', 'int'], + ['data', '0']], + ['tag', 'config:config-item', + ['element', 'config:name', 'PositionTop'], + ['element', 'config:type', 'int'], + ['data', '0']], + ['tag', 'config:config-item', + ['element', 'config:name', 'PositionBottom'], + ['element', 'config:type', 'int'], + ['data', '0']]]], + ['tag', 'config:config-item', + ['element', 'config:name', 'ActiveTable'], + ['element', 'config:type', 'string'], + ['data', 'Sheet1']], + ['tag', 'config:config-item', + ['element', 'config:name', 'HorizontalScrollbarWidth'], + ['element', 'config:type', 'int'], + ['data', '270']], + ['tag', 'config:config-item', + ['element', 'config:name', 'ZoomType'], + ['element', 'config:type', 'short'], + ['data', '0']], + ['tag', 'config:config-item', + ['element', 'config:name', 'ZoomValue'], + ['element', 'config:type', 'int'], + ['data', '100']], + ['tag', 'config:config-item', + ['element', 'config:name', 'PageViewZoomValue'], + ['element', 'config:type', 'int'], + ['data', '60']], + ['tag', 'config:config-item', + ['element', 'config:name', 'ShowPageBreakPreview'], + ['element', 'config:type', 'boolean'], + ['data', 'false']], + ['tag', 'config:config-item', + ['element', 'config:name', 'ShowZeroValues'], + ['element', 'config:type', 'boolean'], + ['data', 'true']], + ['tag', 'config:config-item', + ['element', 'config:name', 'ShowNotes'], + ['element', 'config:type', 'boolean'], + ['data', 'true']], + ['tag', 'config:config-item', + ['element', 'config:name', 'ShowGrid'], + ['element', 'config:type', 'boolean'], + ['data', 'true']], + ['tag', 'config:config-item', + ['element', 'config:name', 'GridColor'], + ['element', 'config:type', 'long'], + ['data', '12632256']], + ['tag', 'config:config-item', + ['element', 'config:name', 'ShowPageBreaks'], + ['element', 'config:type', 'boolean'], + ['data', 'true']], + ['tag', 'config:config-item', + ['element', 'config:name', 'HasColumnRowHeaders'], + ['element', 'config:type', 'boolean'], + ['data', 'true']], + ['tag', 'config:config-item', + ['element', 'config:name', 'HasSheetTabs'], + ['element', 'config:type', 'boolean'], + ['data', 'true']], + ['tag', 'config:config-item', + ['element', 'config:name', 'IsOutlineSymbolsSet'], + ['element', 'config:type', 'boolean'], + ['data', 'true']], + ['tag', 'config:config-item', + ['element', 'config:name', 'IsSnapToRaster'], + ['element', 'config:type', 'boolean'], + ['data', 'false']], + ['tag', 'config:config-item', + ['element', 'config:name', 'RasterIsVisible'], + ['element', 'config:type', 'boolean'], + ['data', 'false']], + ['tag', 'config:config-item', + ['element', 'config:name', 'RasterResolutionX'], + ['element', 'config:type', 'int'], + ['data', '1270']], + ['tag', 'config:config-item', + ['element', 'config:name', 'RasterResolutionY'], + ['element', 'config:type', 'int'], + ['data', '1270']], + ['tag', 'config:config-item', + ['element', 'config:name', 'RasterSubdivisionX'], + ['element', 'config:type', 'int'], + ['data', '1']], + ['tag', 'config:config-item', + ['element', 'config:name', 'RasterSubdivisionY'], + ['element', 'config:type', 'int'], + ['data', '1']], + ['tag', 'config:config-item', + ['element', 'config:name', 'IsRasterAxisSynchronized'], + ['element', 'config:type', 'boolean'], + ['data', 'true']]]]], + ['tag', 'config:config-item-set', + ['element', 'config:name', 'ooo:configuration-settings'], + ['tag', 'config:config-item', + ['element', 'config:name', 'ShowZeroValues'], + ['element', 'config:type', 'boolean'], + ['data', 'true']], + ['tag', 'config:config-item', + ['element', 'config:name', 'ShowNotes'], + ['element', 'config:type', 'boolean'], + ['data', 'true']], + ['tag', 'config:config-item', + ['element', 'config:name', 'ShowGrid'], + ['element', 'config:type', 'boolean'], + ['data', 'true']], + ['tag', 'config:config-item', + ['element', 'config:name', 'GridColor'], + ['element', 'config:type', 'long'], + ['data', '12632256']], + ['tag', 'config:config-item', + ['element', 'config:name', 'ShowPageBreaks'], + ['element', 'config:type', 'boolean'], + ['data', 'true']], + ['tag', 'config:config-item', + ['element', 'config:name', 'LinkUpdateMode'], + ['element', 'config:type', 'short'], + ['data', '3']], + ['tag', 'config:config-item', + ['element', 'config:name', 'HasColumnRowHeaders'], + ['element', 'config:type', 'boolean'], + ['data', 'true']], + ['tag', 'config:config-item', + ['element', 'config:name', 'HasSheetTabs'], + ['element', 'config:type', 'boolean'], + ['data', 'true']], + ['tag', 'config:config-item', + ['element', 'config:name', 'IsOutlineSymbolsSet'], + ['element', 'config:type', 'boolean'], + ['data', 'true']], + ['tag', 'config:config-item', + ['element', 'config:name', 'IsSnapToRaster'], + ['element', 'config:type', 'boolean'], + ['data', 'false']], + ['tag', 'config:config-item', + ['element', 'config:name', 'RasterIsVisible'], + ['element', 'config:type', 'boolean'], + ['data', 'false']], + ['tag', 'config:config-item', + ['element', 'config:name', 'RasterResolutionX'], + ['element', 'config:type', 'int'], + ['data', '1270']], + ['tag', 'config:config-item', + ['element', 'config:name', 'RasterResolutionY'], + ['element', 'config:type', 'int'], + ['data', '1270']], + ['tag', 'config:config-item', + ['element', 'config:name', 'RasterSubdivisionX'], + ['element', 'config:type', 'int'], + ['data', '1']], + ['tag', 'config:config-item', + ['element', 'config:name', 'RasterSubdivisionY'], + ['element', 'config:type', 'int'], + ['data', '1']], + ['tag', 'config:config-item', + ['element', 'config:name', 'IsRasterAxisSynchronized'], + ['element', 'config:type', 'boolean'], + ['data', 'true']], + ['tag', 'config:config-item', + ['element', 'config:name', 'AutoCalculate'], + ['element', 'config:type', 'boolean'], + ['data', 'true']], + ['tag', 'config:config-item', + ['element', 'config:name', 'PrinterName'], + ['element', 'config:type', 'string'], + ['data', 'Generic Printer']], + ['tag', 'config:config-item', + ['element', 'config:name', 'PrinterSetup'], + ['element', 'config:type', 'base64Binary'], + ['data', 'YgH+/0dlbmVyaWMgUHJpbnRlcgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU0dFTlBSVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWAAMAqAAAAAAA//8FAFZUAAAkbQAASm9iRGF0YSAxCnByaW50ZXI9R2VuZXJpYyBQcmludGVyCm9yaWVudGF0aW9uPVBvcnRyYWl0CmNvcGllcz0xCnNjYWxlPTEwMAptYXJnaW5kYWp1c3RtZW50PTAsMCwwLDAKY29sb3JkZXB0aD0yNApwc2xldmVsPTAKY29sb3JkZXZpY2U9MApQUERDb250ZXhEYXRhClBhZ2VTaXplOkxldHRlcgAA']], + ['tag', 'config:config-item', + ['element', 'config:name', 'ApplyUserData'], + ['element', 'config:type', 'boolean'], + ['data', 'true']], + ['tag', 'config:config-item', + ['element', 'config:name', 'CharacterCompressionType'], + ['element', 'config:type', 'short'], + ['data', '0']], + ['tag', 'config:config-item', + ['element', 'config:name', 'IsKernAsianPunctuation'], + ['element', 'config:type', 'boolean'], + ['data', 'false']], + ['tag', 'config:config-item', + ['element', 'config:name', 'SaveVersionOnClose'], + ['element', 'config:type', 'boolean'], + ['data', 'false']], + ['tag', 'config:config-item', + ['element', 'config:name', 'UpdateFromTemplate'], + ['element', 'config:type', 'boolean'], + ['data', 'false']], + ['tag', 'config:config-item', + ['element', 'config:name', 'AllowPrintJobCancel'], + ['element', 'config:type', 'boolean'], + ['data', 'true']], + ['tag', 'config:config-item', + ['element', 'config:name', 'LoadReadonly'], + ['element', 'config:type', 'boolean'], + ['data', 'false']]]]] + + # Generate content.xml XML data + xml = XML() + self.lines = xml.convert(self.data) + self.filedata = '\n'.join(self.lines) + # Return generated data + return self.filedata + + + def _ods_styles(self): + "Generate ods styles.xml data" + self.data = ['tag', 'office:document-styles', + ['element', 'xmlns:office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'], + ['element', 'xmlns:style', 'urn:oasis:names:tc:opendocument:xmlns:style:1.0'], + ['element', 'xmlns:text', 'urn:oasis:names:tc:opendocument:xmlns:text:1.0'], + ['element', 'xmlns:table', 'urn:oasis:names:tc:opendocument:xmlns:table:1.0'], + ['element', 'xmlns:draw', 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0'], + ['element', 'xmlns:fo', 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0'], + ['element', 'xmlns:xlink', 'http://www.w3.org/1999/xlink'], + ['element', 'xmlns:dc', 'http://purl.org/dc/elements/1.1/'], + ['element', 'xmlns:meta', 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0'], + ['element', 'xmlns:number', 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0'], + ['element', 'xmlns:svg', 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0'], + ['element', 'xmlns:chart', 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0'], + ['element', 'xmlns:dr3d', 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0'], + ['element', 'xmlns:math', 'http://www.w3.org/1998/Math/MathML'], + ['element', 'xmlns:form', 'urn:oasis:names:tc:opendocument:xmlns:form:1.0'], + ['element', 'xmlns:script', 'urn:oasis:names:tc:opendocument:xmlns:script:1.0'], + ['element', 'xmlns:ooo', 'http://openoffice.org/2004/office'], + ['element', 'xmlns:ooow', 'http://openoffice.org/2004/writer'], + ['element', 'xmlns:oooc', 'http://openoffice.org/2004/calc'], + ['element', 'xmlns:dom', 'http://www.w3.org/2001/xml-events'], + ['element', 'office:version', '1.0'], + ['tag', 'office:font-face-decls', + ['tagline', 'style:font-face', + ['element', 'style:name', 'DejaVu Sans'], + ['element', 'svg:font-family', ''DejaVu Sans''], + ['element', 'style:font-pitch', 'variable']], + ['tagline', 'style:font-face', + ['element', 'style:name', 'Nimbus Sans L'], + ['element', 'svg:font-family', ''Nimbus Sans L''], + ['element', 'style:font-family-generic', 'swiss'], + ['element', 'style:font-pitch', 'variable']]], + ['tag', 'office:styles', + ['tag', 'style:default-style', + ['element', 'style:family', 'table-cell'], + ['tagline', 'style:table-cell-properties', + ['element', 'style:decimal-places', '2']], + ['tagline', 'style:paragraph-properties', + ['element', 'style:tab-stop-distance', '0.5in']], + ['tagline', 'style:text-properties', + ['element', 'style:font-name', 'Nimbus Sans L'], + ['element', 'fo:language', 'en'], + ['element', 'fo:country', 'US'], + ['element', 'style:font-name-asian', 'DejaVu Sans'], + ['element', 'style:language-asian', 'none'], + ['element', 'style:country-asian', 'none'], + ['element', 'style:font-name-complex', 'DejaVu Sans'], + ['element', 'style:language-complex', 'none'], + ['element', 'style:country-complex', 'none']]], + ['tag', 'number:number-style', + ['element', 'style:name', 'N0'], + ['tagline', 'number:number', + ['element', 'number:min-integer-digits', '1']]], + ['tag', 'number:currency-style', + ['element', 'style:name', 'N104P0'], + ['element', 'style:volatile', 'true'], + ['tag', 'number:currency-symbol', + ['element', 'number:language', 'en'], + ['element', 'number:country', 'US'], + ['data', '$']], + ['tagline', 'number:number', + ['element', 'number:decimal-places', '2'], + ['element', 'number:min-integer-digits', '1'], + ['element', 'number:grouping', 'true']]], + ['tag', 'number:currency-style', + ['element', 'style:name', 'N104'], + ['tagline', 'style:text-properties', + ['element', 'fo:color', '#ff0000']], + ['tag', 'number:text', + ['data', '-']], + ['tag', 'number:currency-symbol', + ['element', 'number:language', 'en'], + ['element', 'number:country', 'US'], + ['data', '$']], + ['tagline', 'number:number', + ['element', 'number:decimal-places', '2'], + ['element', 'number:min-integer-digits', '1'], + ['element', 'number:grouping', 'true']], + ['tagline', 'style:map', + ['element', 'style:condition', 'value()>=0'], + ['element', 'style:apply-style-name', 'N104P0']]], + ['tagline', 'style:style', + ['element', 'style:name', 'Default'], + ['element', 'style:family', 'table-cell']], + ['tag', 'style:style', + ['element', 'style:name', 'Result'], + ['element', 'style:family', 'table-cell'], + ['element', 'style:parent-style-name', 'Default'], + ['tagline', 'style:text-properties', + ['element', 'fo:font-style', 'italic'], + ['element', 'style:text-underline-style', 'solid'], + ['element', 'style:text-underline-width', 'auto'], + ['element', 'style:text-underline-color', 'font-color'], + ['element', 'fo:font-weight', 'bold']]], + ['tagline', 'style:style', + ['element', 'style:name', 'Result2'], + ['element', 'style:family', 'table-cell'], + ['element', 'style:parent-style-name', 'Result'], + ['element', 'style:data-style-name', 'N104']], + ['tag', 'style:style', + ['element', 'style:name', 'Heading'], + ['element', 'style:family', 'table-cell'], + ['element', 'style:parent-style-name', 'Default'], + ['tagline', 'style:table-cell-properties', + ['element', 'style:text-align-source', 'fix'], + ['element', 'style:repeat-content', 'false']], + ['tagline', 'style:paragraph-properties', + ['element', 'fo:text-align', 'center']], + ['tagline', 'style:text-properties', + ['element', 'fo:font-size', '16pt'], + ['element', 'fo:font-style', 'italic'], + ['element', 'fo:font-weight', 'bold']]], + ['tag', 'style:style', + ['element', 'style:name', 'Heading1'], + ['element', 'style:family', 'table-cell'], + ['element', 'style:parent-style-name', 'Heading'], + ['tagline', 'style:table-cell-properties', + ['element', 'style:rotation-angle', '90']]]], + ['tag', 'office:automatic-styles', + ['tag', 'style:page-layout', + ['element', 'style:name', 'pm1'], + ['tagline', 'style:page-layout-properties', + ['element', 'style:writing-mode', 'lr-tb']], + ['tag', 'style:header-style', + ['tagline', 'style:header-footer-properties', + ['element', 'fo:min-height', '0.2957in'], + ['element', 'fo:margin-left', '0in'], + ['element', 'fo:margin-right', '0in'], + ['element', 'fo:margin-bottom', '0.0984in']]], + ['tag', 'style:footer-style', + ['tagline', 'style:header-footer-properties', + ['element', 'fo:min-height', '0.2957in'], + ['element', 'fo:margin-left', '0in'], + ['element', 'fo:margin-right', '0in'], + ['element', 'fo:margin-top', '0.0984in']]]], + ['tag', 'style:page-layout', + ['element', 'style:name', 'pm2'], + ['tagline', 'style:page-layout-properties', + ['element', 'style:writing-mode', 'lr-tb']], + ['tag', 'style:header-style', + ['tag', 'style:header-footer-properties', + ['element', 'fo:min-height', '0.2957in'], + ['element', 'fo:margin-left', '0in'], + ['element', 'fo:margin-right', '0in'], + ['element', 'fo:margin-bottom', '0.0984in'], + ['element', 'fo:border', '0.0346in solid #000000'], + ['element', 'fo:padding', '0.0071in'], + ['element', 'fo:background-color', '#c0c0c0'], + ['tagline', 'style:background-image']]], + ['tag', 'style:footer-style', + ['tag', 'style:header-footer-properties', + ['element', 'fo:min-height', '0.2957in'], + ['element', 'fo:margin-left', '0in'], + ['element', 'fo:margin-right', '0in'], + ['element', 'fo:margin-top', '0.0984in'], + ['element', 'fo:border', '0.0346in solid #000000'], + ['element', 'fo:padding', '0.0071in'], + ['element', 'fo:background-color', '#c0c0c0'], + ['tagline', 'style:background-image']]]]], + ['tag', 'office:master-styles', + ['tag', 'style:master-page', + ['element', 'style:name', 'Default'], + ['element', 'style:page-layout-name', 'pm1'], + ['tag', 'style:header', + ['tag', 'text:p', + ['data', '<text:sheet-name>???</text:sheet-name>']]], + ['tagline', 'style:header-left', + ['element', 'style:display', 'false']], + ['tag', 'style:footer', + ['tag', 'text:p', + ['data', 'Page <text:page-number>1</text:page-number>']]], + ['tagline', 'style:footer-left', + ['element', 'style:display', 'false']]], + ['tag', 'style:master-page', + ['element', 'style:name', 'Report'], + ['element', 'style:page-layout-name', 'pm2'], + ['tag', 'style:header', + ['tag', 'style:region-left', + ['tag', 'text:p', + ['data', '<text:sheet-name>???</text:sheet-name> (<text:title>???</text:title>)']]], + ['tag', 'style:region-right', + ['tag', 'text:p', + ['data', '<text:date style:data-style-name="N2" text:date-value="2006-09-29">09/29/2006</text:date>, <text:time>13:02:56</text:time>']]]], + ['tagline', 'style:header-left', + ['element', 'style:display', 'false']], + ['tag', 'style:footer', + ['tag', 'text:p', + ['data', 'Page <text:page-number>1</text:page-number> / <text:page-count>99</text:page-count>']]], + ['tagline', 'style:footer-left', + ['element', 'style:display', 'false']]]]] + + + # Generate content.xml XML data + xml = XML() + self.lines = xml.convert(self.data) + self.filedata = '\n'.join(self.lines) + # Return generated data + return self.filedata + +class Writer: + "Writer Class - Used to create OpenDocument Format Writer Documents." + def __init__(self): + "Initialize ooolib Writer instance" + # Default to no debugging + self.debug = False + self.meta = Meta('odt') + + def set_meta(self, metaname, value): + "Set meta data in your document." + self.meta.set_meta(metaname, value) + + def save(self, filename): + """Save .odt document + + The save function saves the current .odt document. + """ + if self.debug: print "Writing %s" % filename + self.savefile = zipfile.ZipFile(filename, "w") + if self.debug: print " meta.xml" + self._zip_insert(self.savefile, "meta.xml", self.meta.get_meta()) + if self.debug: print " mimetype" + self._zip_insert(self.savefile, "mimetype", "application/vnd.oasis.opendocument.text") + if self.debug: print " META-INF/manifest.xml" + self._zip_insert(self.savefile, "META-INF/manifest.xml", self._odt_manifest()) + if self.debug: print " content.xml" + self._zip_insert(self.savefile, "content.xml", self._odt_content()) + if self.debug: print " settings.xml" + # self._zip_insert(self.savefile, "settings.xml", self._odt_settings()) + if self.debug: print " styles.xml" + # self._zip_insert(self.savefile, "styles.xml", self._odt_styles()) + + # We need to close the file now that we are done creating it. + self.savefile.close() + + def _zip_insert(self, file, filename, data): + now = time.localtime(time.time())[:6] + info = zipfile.ZipInfo(filename) + info.date_time = now + info.compress_type = zipfile.ZIP_DEFLATED + file.writestr(info, data) + + def _odt_manifest(self): + "Generate odt manifest.xml data" + + self.data = ['tag', 'manifest:manifest', + ['element', 'xmlns:manifest', 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0'], + ['tagline', 'manifest:file-entry', + ['element', 'manifest:media-type', 'application/vnd.oasis.opendocument.text'], + ['element', 'manifest:full-path', '/']], + ['tagline', 'manifest:file-entry', + ['element', 'manifest:media-type', 'text/xml'], + ['element', 'manifest:full-path', 'content.xml']], + ['tagline', 'manifest:file-entry', + ['element', 'manifest:media-type', 'text/xml'], + ['element', 'manifest:full-path', 'styles.xml']], + ['tagline', 'manifest:file-entry', + ['element', 'manifest:media-type', 'text/xml'], + ['element', 'manifest:full-path', 'meta.xml']], + ['tagline', 'manifest:file-entry', + ['element', 'manifest:media-type', 'text/xml'], + ['element', 'manifest:full-path', 'settings.xml']]] + + # Generate content.xml XML data + xml = XML() + self.lines = xml.convert(self.data) + self.lines.insert(1, '<!DOCTYPE manifest:manifest PUBLIC "-//OpenOffice.org//DTD Manifest 1.0//EN" "Manifest.dtd">') + self.filedata = '\n'.join(self.lines) + # Return generated data + return self.filedata + + def _odt_content(self): + "Generate odt content.xml data" + + self.data = ['tag', 'office:document-content', + ['element', 'xmlns:office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'], + ['element', 'xmlns:style', 'urn:oasis:names:tc:opendocument:xmlns:style:1.0'], + ['element', 'xmlns:text', 'urn:oasis:names:tc:opendocument:xmlns:text:1.0'], + ['element', 'xmlns:table', 'urn:oasis:names:tc:opendocument:xmlns:table:1.0'], + ['element', 'xmlns:draw', 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0'], + ['element', 'xmlns:fo', 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0'], + ['element', 'xmlns:xlink', 'http://www.w3.org/1999/xlink'], + ['element', 'xmlns:dc', 'http://purl.org/dc/elements/1.1/'], + ['element', 'xmlns:meta', 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0'], + ['element', 'xmlns:number', 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0'], + ['element', 'xmlns:svg', 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0'], + ['element', 'xmlns:chart', 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0'], + ['element', 'xmlns:dr3d', 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0'], + ['element', 'xmlns:math', 'http://www.w3.org/1998/Math/MathML'], + ['element', 'xmlns:form', 'urn:oasis:names:tc:opendocument:xmlns:form:1.0'], + ['element', 'xmlns:script', 'urn:oasis:names:tc:opendocument:xmlns:script:1.0'], + ['element', 'xmlns:ooo', 'http://openoffice.org/2004/office'], + ['element', 'xmlns:ooow', 'http://openoffice.org/2004/writer'], + ['element', 'xmlns:oooc', 'http://openoffice.org/2004/calc'], + ['element', 'xmlns:dom', 'http://www.w3.org/2001/xml-events'], + ['element', 'xmlns:xforms', 'http://www.w3.org/2002/xforms'], + ['element', 'xmlns:xsd', 'http://www.w3.org/2001/XMLSchema'], + ['element', 'xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'], + ['element', 'office:version', '1.0'], + ['tagline', 'office:scripts'], + ['tag', 'office:font-face-decls', + ['tagline', 'style:font-face', + ['element', 'style:name', 'DejaVu Sans'], + ['element', 'svg:font-family', ''DejaVu Sans''], + ['element', 'style:font-pitch', 'variable']], + ['tagline', 'style:font-face', + ['element', 'style:name', 'Nimbus Roman No9 L'], + ['element', 'svg:font-family', ''Nimbus Roman No9 L''], + ['element', 'style:font-family-generic', 'roman'], + ['element', 'style:font-pitch', 'variable']], + ['tagline', 'style:font-face', + ['element', 'style:name', 'Nimbus Sans L'], + ['element', 'svg:font-family', ''Nimbus Sans L''], + ['element', 'style:font-family-generic', 'swiss'], + ['element', 'style:font-pitch', 'variable']]], + ['tagline', 'office:automatic-styles'], + ['tag', 'office:body', + ['tag', 'office:text', + ['tagline', 'office:forms', + ['element', 'form:automatic-focus', 'false'], + ['element', 'form:apply-design-mode', 'false']], + ['tag', 'text:sequence-decls', + ['tagline', 'text:sequence-decl', + ['element', 'text:display-outline-level', '0'], + ['element', 'text:name', 'Illustration']], + ['tagline', 'text:sequence-decl', + ['element', 'text:display-outline-level', '0'], + ['element', 'text:name', 'Table']], + ['tagline', 'text:sequence-decl', + ['element', 'text:display-outline-level', '0'], + ['element', 'text:name', 'Text']], + ['tagline', 'text:sequence-decl', + ['element', 'text:display-outline-level', '0'], + ['element', 'text:name', 'Drawing']]], + ['tagline', 'text:p', + ['element', 'text:style-name', 'Standard']]]]] + + # Generate content.xml XML data + xml = XML() + self.lines = xml.convert(self.data) + self.filedata = '\n'.join(self.lines) + # Return generated data + return self.filedata + + diff --git a/contrib/non-profit-audit-reports/readcsv.py b/contrib/non-profit-audit-reports/readcsv.py new file mode 100755 index 00000000..67fc5663 --- /dev/null +++ b/contrib/non-profit-audit-reports/readcsv.py @@ -0,0 +1,31 @@ +#!/usr/bin/python +# readcsv.py +# CSV reading technical study +# +# Copyright (c) 2012 Tom Marble +# +# This program gives you software freedom; you can copy, modify, convey, +# and/or redistribute it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program in a file called 'GPLv3'. If not, write to the: +# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor +# Boston, MA 02110-1301, USA. + +import csv + +dialects = csv.list_dialects() +for dialect in dialects: + print 'dialect %s' % str(dialect) + +csvfile = open('tests/general-ledger.csv', 'rb') +reader = csv.reader(csvfile, delimiter=',', quotechar='"') +for row in reader: + print row diff --git a/contrib/non-profit-audit-reports/summary-reports.plx b/contrib/non-profit-audit-reports/summary-reports.plx new file mode 100755 index 00000000..e9e1a3b8 --- /dev/null +++ b/contrib/non-profit-audit-reports/summary-reports.plx @@ -0,0 +1,464 @@ +#!/usr/bin/perl +# fund-report.plx -*- Perl -*- +# +# Script to generate end-of-year summary reports. +# +# Copyright (C) 2011, 2012, 2013, Bradley M. Kuhn +# +# This program gives you software freedom; you can copy, modify, convey, +# and/or redistribute it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program in a file called 'GPLv3'. If not, write to the: +# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor +# Boston, MA 02110-1301, USA. + +use strict; +use warnings; + +use Math::BigFloat; +use Date::Manip; + +my $VERBOSE = 0; +my $DEBUG = 0; + +my $LEDGER_BIN = "/usr/local/bin/ledger"; + +my $ACCT_WIDTH = 70; + +sub Commify ($) { + my $text = reverse $_[0]; + $text =~ s/(\d\d\d)(?=\d)(?!\d*\.)/$1,/g; + return scalar reverse $text; +} + +sub preferredAccountSorting ($$) { + if ($_[0] =~ /^Assets/ and $_[1] !~ /^Assets/) { + return -1; + } elsif ($_[1] =~ /^Assets/ and $_[0] !~ /^Assets/) { + return 1; + } elsif ($_[0] =~ /^Liabilities/ and $_[1] !~ /^(Assets|Liabilities)/) { + return -1; + } elsif ($_[1] =~ /^Liabilities/ and $_[0] !~ /^(Assets|Liabilities)/) { + return 1; + } elsif ($_[0] =~ /^(Accrued:[^:]+Receivable)/ and $_[1] !~ /^(Assets|Liabilities|Accrued:[^:]+Receivable)/) { + return -1; + } elsif ($_[1] =~ /^(Accrued:[^:]+Receivable)/ and $_[0] !~ /^(Assets|Liabilities|Accrued:[^:]+Receivable)/) { + return 1; + } elsif ($_[0] =~ /^(Accrued)/ and $_[1] !~ /^(Assets|Liabilities|Accrued)/) { + return -1; + } elsif ($_[1] =~ /^(Accrued)/ and $_[0] !~ /^(Assets|Liabilities|Accrued)/) { + return 1; + } elsif ($_[0] =~ /^(Unearned Income)/ and $_[1] !~ /^(Assets|Liabilities|Accrued|Unearned Income)/) { + return -1; + } elsif ($_[1] =~ /^(Unearned Income)/ and $_[0] !~ /^(Assets|Liabilities|Accrued|Unearned Income)/) { + return 1; + } elsif ($_[0] =~ /^Income/ and $_[1] !~ /^(Assets|Liabilities|Accrued|Unearned Income|Income)/) { + return -1; + } elsif ($_[1] =~ /^Income/ and $_[0] !~ /^(Assets|Liabilities|Accrued|Unearned Income|Income)/) { + return 1; + } elsif ($_[0] =~ /^Expense/ and $_[1] !~ /^(Assets|Liabilities|Accrued|Income|Unearned Income|Expense)/) { + return -1; + } elsif ($_[1] =~ /^Expense/ and $_[0] !~ /^(Assets|Liabilities|Accrued|Income|Unearned Income|Expense)/) { + return 1; + } else { + return $_[0] cmp $_[1]; + } +} + +sub ParseNumber($) { + $_[0] =~ s/,//g; + return Math::BigFloat->new($_[0]); +} +Math::BigFloat->precision(-2); +my $ZERO = Math::BigFloat->new("0.00"); +my $ONE_PENNY = Math::BigFloat->new("0.01"); + +if (@ARGV < 2) { + print STDERR "usage: $0 <START_DATE> <END_DATE> <LEDGER_OPTIONS>\n"; + exit 1; +} +my($startDate, $endDate, @mainLedgerOptions) = @ARGV; + +my $err; +my $formattedEndDate = UnixDate(DateCalc(ParseDate($endDate), ParseDateDelta("- 1 day"), \$err), + "%B %e, %Y"); +die "Date calculation error on $endDate" if ($err); +my $formattedStartDate = UnixDate(ParseDate($startDate), "%B %e, %Y"); +die "Date calculation error on $startDate" if ($err); + +my %reportFields = + ('Cash' => { args => [ '-e', $endDate, 'bal', '/^Assets/' ] }, + 'Accounts Receivable' => {args => [ '-e', $endDate, 'bal', '/^Accrued:Accounts Receivable/' ]}, + 'Loans Receivable' => {args => [ '-e', $endDate, 'bal', '/^Accrued:Loans Receivable/' ]}, + 'Accounts Payable' => {args => [ '-e', $endDate, 'bal', '/^Accrued.*Accounts Payable/' ]}, + 'Accrued Expenses' => {args => [ '-e', $endDate, 'bal', '/^Accrued.*Expenses/' ]}, + 'Liabilities, Credit Cards' => {args => [ '-e', $endDate, 'bal', '/^Liabilities:Credit Card/' ]}, + 'Liabilities, Other' => {args => [ '-e', $endDate, 'bal', '/^Liabilities/', + 'and', 'not', '/^Liabilities:Credit Card/']}, + 'Unearned Income, Conference Registration' => {args => [ '-e', $endDate, 'bal', + '/^Unearned Income.*Conf.*Reg/' ]}, + 'Unearned Income, Other' => {args => [ '-e', $endDate, 'bal', '/^Unearned Income/', 'and', 'not', + '/^Unearned Income.*Conf.*Reg/' ]}, + 'Unrestricted Net Assets' => {args => [ '-e', $endDate, 'bal', '/^(Income|Expenses):Conservancy/' ]}, + 'Temporarily Restricted Net Assets' => {args => [ '-e', $endDate, 'bal', '/^(Income|Expenses)/', + 'and', 'not', '/^(Unearned Income|(Income|Expenses):Conservancy)/' ]}, + 'Total Net Assets' => {args => [ '-e', $endDate, 'bal', '/^(Income|Expenses)/' ]}, + +); +foreach my $item (keys %reportFields) { + my(@fullCommand) = ($LEDGER_BIN, @mainLedgerOptions, + '-V', '-X', '$', '-S', 'T', '-s', '-d', 'T', @{$reportFields{$item}{args}}); + open(FILE, "-|", @fullCommand) + or die "unable to run command ledger command: @fullCommand: $!"; + + my $foundBalance; + my $seenTotalLine = 0; + + print STDERR ($VERBOSE ? "Running: @fullCommand\n" : "."); + print STDERR " Output of @fullCommand\n" if $DEBUG; + + while (my $line = <FILE>) { + print STDERR $line if ($DEBUG); + + $seenTotalLine = 1 if $line =~ /^\s*\-+\s*/; # Skip lines until the total line + $foundBalance = $1 + if (not $seenTotalLine and $line =~ /^\s*[^0-9\-]+\s*([\-\d,\.]+)\s+/); + + if ($line =~ /^\s*\$\s*([\-\d,\.]+)\s*$/) { + $foundBalance = $1; + last; + } + } + close FILE; + die "problem running ledger command: @fullCommand: $!" unless ($? == 0); + if (not defined $foundBalance) { + $foundBalance = $ZERO; + } else { + $foundBalance =~ s/,//g; + $foundBalance = Math::BigFloat->new($foundBalance); + } + $foundBalance = $ZERO if not defined $foundBalance; + $reportFields{$item}{total} = abs($foundBalance); + print STDERR "$item: $reportFields{$item}{total}\n" if $VERBOSE; +} + +open(BALANCE_SHEET, ">", "balance-sheet.csv") + or die "unable to open balance-sheet.csv for writing: $!"; + +print BALANCE_SHEET "\"BALANCE SHEET\"\n", + "\"Ending\",\"", $formattedEndDate, "\"\n", + "\n\n\"ASSETS\"\n\n"; + +my $formatStr = "\"\",\"%-42s\",\"\$%13s\"\n"; +my $formatStrTotal = "\"\",\"%-45s\",\"\$%13s\"\n"; +my $tot = $ZERO; +foreach my $item ('Cash', 'Accounts Receivable', 'Loans Receivable') { + next if $reportFields{$item}{total} == $ZERO; + print BALANCE_SHEET sprintf($formatStr, "$item:", Commify($reportFields{$item}{total})); + $tot += $reportFields{$item}{total}; +} +print BALANCE_SHEET "\n", sprintf($formatStrTotal, "TOTAL ASSETS", Commify($tot)), "\n\nLIABILITIES\n\n"; + +my $totLiabilities = $ZERO; +foreach my $item ('Accounts Payable', 'Accrued Expenses', + 'Liabilities, Credit Cards', 'Liabilities, Other', + 'Unearned Income, Conference Registration', 'Unearned Income, Other') { + next if $reportFields{$item}{total} == $ZERO; + print BALANCE_SHEET sprintf($formatStr, "$item:", Commify($reportFields{$item}{total})); + $totLiabilities += $reportFields{$item}{total}; +} +print BALANCE_SHEET "\n", sprintf($formatStr, "TOTAL LIABILTIES", Commify($totLiabilities)), + "\n\nNET ASSETS\n\n"; + +my $totNetAssets = $ZERO; +foreach my $item ('Unrestricted Net Assets', 'Temporarily Restricted Net Assets') { + next if $reportFields{$item}{total} == $ZERO; + print BALANCE_SHEET sprintf($formatStr, "$item:", Commify($reportFields{$item}{total})); + $totNetAssets += $reportFields{$item}{total}; +} +print BALANCE_SHEET "\n", sprintf($formatStr, "TOTAL NET ASSETS", Commify($totNetAssets)), "\n\n", + sprintf($formatStrTotal, "TOTAL LIABILITIES AND NET ASSETS", + Commify($totNetAssets + $totLiabilities)); + +close BALANCE_SHEET; +print STDERR "\n"; +die "unable to write to balance-sheet.csv: $!" unless ($? == 0); + +die "Cash+accounts receivable total does not equal net assets and liabilities total" + if (abs( ($reportFields{'Cash'}{total} + $reportFields{'Accounts Receivable'}{total} + + $reportFields{'Loans Receivable'}{total})) - + abs($reportFields{'Accounts Payable'}{total} + + $reportFields{'Accrued Expenses'}{total} + + $reportFields{'Unearned Income, Conference Registration'}{total} + + $reportFields{'Unearned Income, Other'}{total} + + $reportFields{'Liabilities, Credit Cards'}{total} + + $reportFields{'Liabilities, Other'}{total} + + $reportFields{'Total Net Assets'}{total}) > $ONE_PENNY); + +die "Total net assets doesn't equal sum of restricted and unrestricted ones!" + if (abs($reportFields{'Total Net Assets'}{total}) - + abs($reportFields{'Unrestricted Net Assets'}{total} + + $reportFields{'Temporarily Restricted Net Assets'}{total}) > $ONE_PENNY); + + +my %incomeGroups = ('INTEREST INCOME' => { args => ['/^Income.*Interest/' ] }, + 'DONATIONS' => { args => [ '/^Income.*Donation/' ] }, + 'BOOK ROYALTIES & AFFILIATE PROGRAMS' => + { args => [ '/^Income.*(Royalt|Affilate)/' ] }, + 'CONFERENCES, REGISTRATION' => {args => [ '/^Income.*Conf.*Reg/' ] }, + 'CONFERENCES, RELATED BUSINESS INCOME' => { args => [ '/^Income.*(Booth|RBI)/'] }, + 'LICENSE ENFORCEMENT' => { args => [ '/^Income.*Enforce/' ]}, + 'TRADEMARKS' => {args => [ '/^Income.*Trademark/' ]}, + 'ADVERSITING' => {args => [ '/^Income.*Advertising/' ]}); + +my @otherArgs; +foreach my $type (keys %incomeGroups) { + @otherArgs = ("/^Income/") if @otherArgs == 0; + push(@otherArgs, 'and', 'not', @{$incomeGroups{$type}{args}}); +} +$incomeGroups{"OTHER"}{args} = \@otherArgs; +$incomeGroups{"TOTAL"}{args} = ['/^Income/']; + +open(INCOME, ">", "income.csv") or die "unable to open income.csv for writing: $!"; + +foreach my $type (keys %incomeGroups) { + my(@fullCommand) = ($LEDGER_BIN, @mainLedgerOptions, '-V', '-X', '$', + '-b', $startDate, '-e', $endDate, + '-F', '%-.80A %22.108t\n', '-s', + 'reg', @{$incomeGroups{$type}{args}}); + + open(FILE, "-|", @fullCommand) + or die "unable to run command ledger command: @fullCommand: $!"; + + print STDERR ($VERBOSE ? "Running: @fullCommand\n" : "."); + + $incomeGroups{$type}{total} = $ZERO; + $incomeGroups{$type}{output} = ""; + + foreach my $line (<FILE>) { + die "Unable to parse output line from second funds command: $line" + unless $line =~ /^\s*([^\$]+)\s+\$\s*([\-\d\.\,]+)/; + my($account, $amount) = ($1, $2); + $amount = ParseNumber($amount); + $account =~ s/\s+$//; + next if $account =~ /\<Adjustment\>/ and (abs($amount) <= 0.02); + die "Weird account found, $account with amount of $amount in income command\n" + unless $account =~ /^\s*Income:/; + + $incomeGroups{$type}{total} += $amount; + $incomeGroups{$type}{output} .= "\"$account\",\"\$$amount\"\n"; + } +} +print INCOME "\"INCOME\",", + "\"STARTING:\",\"$formattedStartDate\",\"ENDING:\",\"$formattedEndDate\"\n\n"; + + +my $overallTotal = $ZERO; + +$formatStrTotal = "\"%-90s\",\"\$%14s\"\n"; +foreach my $type ('DONATIONS', 'LICENSE ENFORCEMENT', + 'CONFERENCES, REGISTRATION', 'CONFERENCES, RELATED BUSINESS INCOME', + 'BOOK ROYALTIES & AFFILIATE PROGRAMS', 'ADVERSITING', + 'TRADEMARKS', 'INTEREST INCOME', 'OTHER') { + next if ($incomeGroups{$type}{output} =~ /^\s*$/ and $incomeGroups{$type}{total} == $ZERO); + print INCOME "\n\"$type\"\n", + $incomeGroups{$type}{output}, "\n", + sprintf($formatStrTotal, "TOTAL $type:", Commify($incomeGroups{$type}{total})); + $overallTotal += $incomeGroups{$type}{total}; +} +print INCOME "\n\n\n", sprintf($formatStrTotal, "OVERALL TOTAL:", Commify($overallTotal)); + +close INCOME; die "unable to write to income.csv: $!" unless ($? == 0); + +die "calculated total of $overallTotal does equal $incomeGroups{TOTAL}{total}" + if (abs($overallTotal) - abs($incomeGroups{TOTAL}{total}) > $ONE_PENNY); + +print STDERR "\n"; + +my %expenseGroups = ('BANKING FEES' => { regex => '^Expenses.*(Banking Fees|Currency Conversion)' }, + 'COMPUTING, HOSTING AND EQUIPMENT' => { regex => '^Expenses.*(Hosting|Computer Equipment)' }, + 'CONFERENCES' => { regex => '^Expenses.*(Conferences|Sprint)' }, + 'DEVELOPER MENTORING' => {regex => '^Expenses.*Mentor' }, + 'LICENSE ENFORCEMENT' => { regex => '^Expenses.*Enforce' }, + 'ACCOUNTING' => { regex => '^Expenses.*(Accounting|Annual Audit)' }, + 'PAYROLL' => { regex => '^Expenses.*Payroll' }, + 'OFFICE' => { regex => '^Expenses.*(Office|Phones)' }, + 'RENT' => { regex => '^Expenses.*Rent' }, + 'SOFTWARE DEVELOPMENT' => { regex => '^Expenses.*Development' }, + 'OTHER PROGRAM ACTIVITY' => {regex => '^Expenses.*Gould' }, + 'ADVOCACY AND PROMOTION' => {regex => '^Expenses.*(Slipstream|Advocacy Merchandise|Promotional)' }, + 'ADVERSITING' => {regex => '^Expenses.*Advertising' }); + +foreach my $type (keys %expenseGroups, 'TRAVEL') { + $expenseGroups{$type}{total} = $ZERO; + $expenseGroups{$type}{output} = ""; +} + +open(EXPENSE, ">", "expense.csv") or die "unable to open expense.csv for writing: $!"; + +my(@fullCommand) = ($LEDGER_BIN, @mainLedgerOptions, '-V', '-X', '$', + '-b', $startDate, '-e', $endDate, + '-F', '%-.80A %22.108t\n', '-s', + 'reg', '/^Expenses/'); + +open(FILE, "-|", @fullCommand) + or die "unable to run command ledger command: @fullCommand: $!"; + +print STDERR ($VERBOSE ? "Running: @fullCommand\n" : "."); + +my $firstTotal = $ZERO; +foreach my $line (<FILE>) { + die "Unable to parse output line from second funds command: $line" + unless $line =~ /^\s*([^\$]+)\s+\$\s*([\-\d\.\,]+)/; + my($account, $amount) = ($1, $2); + $amount = ParseNumber($amount); + $account =~ s/\s+$//; + next if $account =~ /\<Adjustment\>/ and (abs($amount) <= 0.02); + die "Weird account found, $account, with amount of $amount in expenses command\n" + unless $account =~ /^\s*Expenses:/; + + my $outputLine = "\"$account\",\"\$$amount\"\n"; + my $taken = 0; + # Note: Prioritize to put things under conference expenses if they were for a conference. + foreach my $type ('CONFERENCES', keys %expenseGroups) { + last if $taken; + next if $type eq 'TRAVEL' or $type eq 'OTHER'; + next unless $line =~ /$expenseGroups{$type}{regex}/; + $taken = 1; + $expenseGroups{$type}{total} += $amount; + $expenseGroups{$type}{output} .= $outputLine; + } + if (not $taken) { + if ($account =~ /Travel/) { + $expenseGroups{'TRAVEL'}{total} += $amount; + $expenseGroups{'TRAVEL'}{output} .= $outputLine; + } else { + $expenseGroups{'OTHER'}{total} += $amount; + $expenseGroups{'OTHER'}{output} .= $outputLine; + } + } + $firstTotal += $amount; +} +print EXPENSE "\"EXPENSES\",", + "\"STARTING:\",\"$formattedStartDate\",\"ENDING:\",\"$formattedEndDate\"\n\n"; +$overallTotal = $ZERO; +$formatStrTotal = "\"%-90s\",\"\$%14s\"\n"; + +my %verifyAllGroups; +foreach my $key (keys %expenseGroups) { + $verifyAllGroups{$key} = 1; +} +foreach my $type ('PAYROLL', 'SOFTWARE DEVELOPMENT', 'LICENSE ENFORCEMENT', 'CONFERENCES', + 'DEVELOPER MENTORING', 'TRAVEL', 'BANKING FEES', 'ADVOCACY AND PROMOTION', + 'COMPUTING, HOSTING AND EQUIPMENT', 'ACCOUNTING', + 'OFFICE', 'RENT', 'ADVERSITING', 'OTHER PROGRAM ACTIVITY', 'OTHER') { + delete $verifyAllGroups{$type}; + + die "$type is not defined!" if not defined $expenseGroups{$type}; + next if ($expenseGroups{$type}{output} =~ /^\s*$/ and $expenseGroups{$type}{total} == $ZERO); + + print EXPENSE "\n\"$type\"\n", + $expenseGroups{$type}{output}, "\n", + sprintf($formatStrTotal, "TOTAL $type:", Commify($expenseGroups{$type}{total})); + $overallTotal += $expenseGroups{$type}{total}; +} + +print EXPENSE "\n\n\n", sprintf($formatStrTotal, "OVERALL TOTAL:", Commify($overallTotal)); + +close EXPENSE; die "unable to write to expense.csv: $!" unless ($? == 0); + +die "GROUPS NOT INCLUDED : ", join(keys(%verifyAllGroups), ", "), "\n" + unless (keys %verifyAllGroups == 0); + +die "calculated total of $overallTotal does equal $firstTotal" + if (abs($overallTotal) - abs($firstTotal) > $ONE_PENNY); + +print STDERR "\n"; + +open(TRIAL, ">", "trial-balance.csv") or die "unable to open accrued.txt for writing: $!"; + +print TRIAL "\"TRIAL BALANCE REPORT\",\"ENDING: $formattedEndDate\"\n\n", + "\"ACCOUNT\",\"BALANCE AT $formattedStartDate\",\"CHANGE DURING FY\",\"BALANCE AT $formattedEndDate\"\n\n"; + +my %commands = ( + 'totalEndFY' => [ $LEDGER_BIN, @mainLedgerOptions, '-V', '-X', '$', + '-e', $endDate, '-F', '%-.80A %22.108t\n', '-s', + 'reg' ], + 'amountInYear' => [ $LEDGER_BIN, @mainLedgerOptions, '-V', '-X', '$', + '-b', $startDate, '-e', $endDate, '-F', '%-.80A %22.108t\n', + '-s', 'reg' ], + 'totalBeginFY' => [ $LEDGER_BIN, @mainLedgerOptions, '-V', '-X', '$', + '-e', $startDate, '-F', '%-.80A %22.108t\n', + '-s', 'reg' ]); + +my %trialBalanceData; +my %fullAccountList; + +foreach my $id (keys %commands) { + my(@command) = @{$commands{$id}}; + + open(FILE, "-|", @command) + or die "unable to run command ledger command: @command: $!"; + + print STDERR ($VERBOSE ? "Running: @command\n" : "."); + + foreach my $line (<FILE>) { + die "Unable to parse output line from trial balance $id command: $line" + unless $line =~ /^\s*([^\$]+)\s+\$\s*([\-\d\.\,]+)/; + my($account, $amount) = ($1, $2); + $amount = ParseNumber($amount); + $account =~ s/\s+$//; + next if $account =~ /\<Adjustment\>/ and (abs($amount) <= 0.02); + next if $account =~ /^Equity:/; # Stupid auto-account made by ledger. + $trialBalanceData{$id}{$account} = $amount; + $fullAccountList{$account} = $id; + } + close FILE; + die "unable to run trial balance ledger command, @command: $!" unless ($? == 0); +} + +my $curOn = 'Assets'; + +foreach my $account (sort preferredAccountSorting keys %fullAccountList) { + # Blank lines right + if ($account !~ /^$curOn/) { + print TRIAL "pagebreak\n"; + $curOn = $account; + if ($curOn =~ /(Accrued:[^:]+):.*$/) { + $curOn = $1; + } else { + $curOn =~ s/^([^:]+):.*$/$1/; + } + } + if ($account =~ /^Assets|Liabilities|Accrued|Unearned Income/) { + foreach my $id (qw/totalBeginFY totalEndFY amountInYear/) { + $trialBalanceData{$id}{$account} = $ZERO + unless defined $trialBalanceData{$id}{$account}; + } + print TRIAL "\"$account\",\"\$$trialBalanceData{totalBeginFY}{$account}\",", + "\"\$$trialBalanceData{amountInYear}{$account}\",\"\$$trialBalanceData{totalEndFY}{$account}\"\n" + unless $trialBalanceData{totalBeginFY}{$account} == $ZERO and + $trialBalanceData{amountInYear}{$account} == $ZERO and + $trialBalanceData{totalEndFY}{$account} == $ZERO; + } else { + print TRIAL "\"$account\",\"\",\"\$$trialBalanceData{amountInYear}{$account}\",\"\"\n" + if defined $trialBalanceData{amountInYear}{$account} and + $trialBalanceData{amountInYear}{$account} != $ZERO; + } +} +close TRIAL; +die "unable to write trial-balance.csv: $!" unless ($? == 0); + +############################################################################### +# +# Local variables: +# compile-command: "perl -c summary-reports.plx" +# End: diff --git a/contrib/non-profit-audit-reports/tests/Financial/BankStuff/bank-statement.pdf b/contrib/non-profit-audit-reports/tests/Financial/BankStuff/bank-statement.pdf Binary files differnew file mode 100644 index 00000000..27b40353 --- /dev/null +++ b/contrib/non-profit-audit-reports/tests/Financial/BankStuff/bank-statement.pdf diff --git a/contrib/non-profit-audit-reports/tests/Financial/Invoices/Invoice20110510.pdf b/contrib/non-profit-audit-reports/tests/Financial/Invoices/Invoice20110510.pdf Binary files differnew file mode 100644 index 00000000..e2e06c98 --- /dev/null +++ b/contrib/non-profit-audit-reports/tests/Financial/Invoices/Invoice20110510.pdf diff --git a/contrib/non-profit-audit-reports/tests/Financial/Invoices/Invoice20110510.txt b/contrib/non-profit-audit-reports/tests/Financial/Invoices/Invoice20110510.txt new file mode 100644 index 00000000..4d5fc907 --- /dev/null +++ b/contrib/non-profit-audit-reports/tests/Financial/Invoices/Invoice20110510.txt @@ -0,0 +1,5 @@ +Invoice + +Date: May 10, 2011 + +Donation to the General Fund: $50.00 diff --git a/contrib/non-profit-audit-reports/tests/Projects/Blah/Expenses/hosting/AprilHostingReceipt.pdf b/contrib/non-profit-audit-reports/tests/Projects/Blah/Expenses/hosting/AprilHostingReceipt.pdf new file mode 100644 index 00000000..b6937670 --- /dev/null +++ b/contrib/non-profit-audit-reports/tests/Projects/Blah/Expenses/hosting/AprilHostingReceipt.pdf @@ -0,0 +1,106 @@ +%PDF-1.4 +%쏢 +5 0 obj +<</Length 6 0 R/Filter /FlateDecode>> +stream +xn0E~1q#]4@;0x؆*tQ!d_Bp:;=F&='(X}]D\+cQٲ K^jyYHTTMq<q#v纹'nn~]c$33Ah{LC
%Ԃq{þVI!JAA*kʼn1]3{f&0"Lbwɿ"XP~v9BVxu`@Yz=t1o+Wʻk/S-/RWQB"5}m?^f|}8?M*2 o 8?Qsv_RGSdrendstream +endobj +6 0 obj +369 +endobj +4 0 obj +<</Type/Page/MediaBox [0 0 612 792] +/Rotate 0/Parent 3 0 R +/Resources<</ProcSet[/PDF /Text] +/ExtGState 11 0 R +/Font 12 0 R +>> +/Contents 5 0 R +>> +endobj +3 0 obj +<< /Type /Pages /Kids [ +4 0 R +] /Count 1 +>> +endobj +1 0 obj +<</Type /Catalog /Pages 3 0 R +/Metadata 13 0 R +>> +endobj +7 0 obj +<</Type/ExtGState +/OPM 1>>endobj +11 0 obj +<</R7 +7 0 R>> +endobj +12 0 obj +<</R9 +9 0 R/R8 +8 0 R/R10 +10 0 R>> +endobj +9 0 obj +<</BaseFont/Helvetica/Type/Font +/Subtype/Type1>> +endobj +8 0 obj +<</BaseFont/Courier/Type/Font +/Subtype/Type1>> +endobj +10 0 obj +<</BaseFont/Helvetica-Bold/Type/Font +/Subtype/Type1>> +endobj +13 0 obj +<</Type/Metadata +/Subtype/XML/Length 1393>>stream +<?xpacket begin='' id='W5M0MpCehiHzreSzNTczkc9d'?> +<?adobe-xap-filters esc="CRLF"?> +<x:xmpmeta xmlns:x='adobe:ns:meta/' x:xmptk='XMP toolkit 2.9.1-13, framework 1.6'> +<rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#' xmlns:iX='http://ns.adobe.com/iX/1.0/'> +<rdf:Description rdf:about='1335afed-313b-11ed-0000-eb02a9a83ec4' xmlns:pdf='http://ns.adobe.com/pdf/1.3/' pdf:Producer='GPL Ghostscript 8.71'/> +<rdf:Description rdf:about='1335afed-313b-11ed-0000-eb02a9a83ec4' xmlns:xmp='http://ns.adobe.com/xap/1.0/'><xmp:ModifyDate>2012-09-07T15:02:10-04:00</xmp:ModifyDate> +<xmp:CreateDate>2012-09-07T15:02:10-04:00</xmp:CreateDate> +<xmp:CreatorTool>a2ps version 4.14</xmp:CreatorTool></rdf:Description> +<rdf:Description rdf:about='1335afed-313b-11ed-0000-eb02a9a83ec4' xmlns:xapMM='http://ns.adobe.com/xap/1.0/mm/' xapMM:DocumentID='1335afed-313b-11ed-0000-eb02a9a83ec4'/> +<rdf:Description rdf:about='1335afed-313b-11ed-0000-eb02a9a83ec4' xmlns:dc='http://purl.org/dc/elements/1.1/' dc:format='application/pdf'><dc:title><rdf:Alt><rdf:li xml:lang='x-default'>receipt</rdf:li></rdf:Alt></dc:title><dc:creator><rdf:Seq><rdf:li>Bradley M. Kuhn</rdf:li></rdf:Seq></dc:creator></rdf:Description> +</rdf:RDF> +</x:xmpmeta> + + +<?xpacket end='w'?> +endstream +endobj +2 0 obj +<</Producer(GPL Ghostscript 8.71) +/CreationDate(D:20120907150210-04'00') +/ModDate(D:20120907150210-04'00') +/Title(receipt) +/Author(Bradley M. Kuhn) +/Creator(a2ps version 4.14)>>endobj +xref +0 14 +0000000000 65535 f +0000000692 00000 n +0000002544 00000 n +0000000633 00000 n +0000000473 00000 n +0000000015 00000 n +0000000454 00000 n +0000000757 00000 n +0000000942 00000 n +0000000878 00000 n +0000001004 00000 n +0000000798 00000 n +0000000828 00000 n +0000001074 00000 n +trailer +<< /Size 14 /Root 1 0 R /Info 2 0 R +/ID [<346C5213A8B2262C0696706A70350365><346C5213A8B2262C0696706A70350365>] +>> +startxref +2736 +%%EOF diff --git a/contrib/non-profit-audit-reports/tests/Projects/Blah/Expenses/hosting/april-invoice.pdf b/contrib/non-profit-audit-reports/tests/Projects/Blah/Expenses/hosting/april-invoice.pdf Binary files differnew file mode 100644 index 00000000..7241909a --- /dev/null +++ b/contrib/non-profit-audit-reports/tests/Projects/Blah/Expenses/hosting/april-invoice.pdf diff --git a/contrib/non-profit-audit-reports/tests/Projects/Foo/Expenses/hosting/AprilHostingReceipt.pdf b/contrib/non-profit-audit-reports/tests/Projects/Foo/Expenses/hosting/AprilHostingReceipt.pdf Binary files differnew file mode 100644 index 00000000..8441f3e6 --- /dev/null +++ b/contrib/non-profit-audit-reports/tests/Projects/Foo/Expenses/hosting/AprilHostingReceipt.pdf diff --git a/contrib/non-profit-audit-reports/tests/Projects/Foo/Expenses/hosting/AprilHostingReceipt.txt b/contrib/non-profit-audit-reports/tests/Projects/Foo/Expenses/hosting/AprilHostingReceipt.txt new file mode 100644 index 00000000..e2722c45 --- /dev/null +++ b/contrib/non-profit-audit-reports/tests/Projects/Foo/Expenses/hosting/AprilHostingReceipt.txt @@ -0,0 +1,6 @@ +Baz Hosting Services, LLC + +Date: April 20, 2011 + +Charge: $250.00 + diff --git a/contrib/non-profit-audit-reports/tests/Projects/Foo/Invoices/Invoice20100101.pdf b/contrib/non-profit-audit-reports/tests/Projects/Foo/Invoices/Invoice20100101.pdf Binary files differnew file mode 100644 index 00000000..11f6286c --- /dev/null +++ b/contrib/non-profit-audit-reports/tests/Projects/Foo/Invoices/Invoice20100101.pdf diff --git a/contrib/non-profit-audit-reports/tests/Projects/Foo/earmark-record.txt b/contrib/non-profit-audit-reports/tests/Projects/Foo/earmark-record.txt new file mode 100644 index 00000000..c5ac98ac --- /dev/null +++ b/contrib/non-profit-audit-reports/tests/Projects/Foo/earmark-record.txt @@ -0,0 +1 @@ +I, Another J. Donor, would like $400 to be earmarked for Foo! diff --git a/contrib/non-profit-audit-reports/tests/non-profit-test-data.ledger b/contrib/non-profit-audit-reports/tests/non-profit-test-data.ledger new file mode 100644 index 00000000..fb6134ff --- /dev/null +++ b/contrib/non-profit-audit-reports/tests/non-profit-test-data.ledger @@ -0,0 +1,28 @@ + +2010/01/01 Kindly T. Donor + Income:Foo:Donation $-100.00 + ;Invoice: Projects/Foo/Invoices/Invoice20100101.pdf + Assets:Checking $100.00 + + +2011/03/15 Another J. Donor + Income:Foo:Donation $-400.00 + ;Approval: Projects/Foo/earmark-record.txt + Assets:Checking $400.00 + +2011/04/20 (1) Baz Hosting Services, LLC + Expenses:Foo:Hosting $250.00 + ;Receipt: Projects/Foo/Expenses/hosting/AprilHostingReceipt.pdf + Assets:Checking $-250.00 + +2011/05/10 Donation to General Fund + Income:Donation $-50.00 + ;Invoice: Financial/Invoices/Invoice20110510.pdf + Assets:Checking $50.00 + +2011/04/20 (2) Baz Hosting Services, LLC + Expenses:Blah:Hosting $250.00 + ;Receipt: Projects/Blah/Expenses/hosting/AprilHostingReceipt.pdf + ;Invoice: Projects/Blah/Expenses/hosting/april-invoice.pdf + Assets:Checking $-250.00 + ;Statement: Financial/BankStuff/bank-statement.pdf diff --git a/contrib/non-profit-audit-reports/tests/non-profit-test-data_MANIFEST b/contrib/non-profit-audit-reports/tests/non-profit-test-data_MANIFEST new file mode 100644 index 00000000..b8bfc107 --- /dev/null +++ b/contrib/non-profit-audit-reports/tests/non-profit-test-data_MANIFEST @@ -0,0 +1,10 @@ +chart-of-accounts.csv +general-ledger.txt +general-ledger.csv +Financial/BankStuff/bank-statement.pdf +Financial/Invoices/Invoice20110510.pdf +Projects/Foo/Invoices/Invoice20100101.pdf +Projects/Foo/earmark-record.txt +Projects/Blah/Expenses/hosting/AprilHostingReceipt.pdf +Projects/Blah/Expenses/hosting/april-invoice.pdf +Projects/Foo/Expenses/hosting/AprilHostingReceipt.pdf diff --git a/contrib/non-profit-audit-reports/tests/non-profit-test-data_chart-of-accounts.csv b/contrib/non-profit-audit-reports/tests/non-profit-test-data_chart-of-accounts.csv new file mode 100644 index 00000000..445bc412 --- /dev/null +++ b/contrib/non-profit-audit-reports/tests/non-profit-test-data_chart-of-accounts.csv @@ -0,0 +1,6 @@ +"CHART OF ACCOUNTS","BEGINNING:","2012/03/01","ENDING:","2012/02/29" +"Assets:Checking" +"Income:Donation" +"Income:Foo:Donation" +"Expenses:Blah:Hosting" +"Expenses:Foo:Hosting" diff --git a/contrib/non-profit-audit-reports/tests/non-profit-test-data_general-ledger.ods b/contrib/non-profit-audit-reports/tests/non-profit-test-data_general-ledger.ods Binary files differnew file mode 100644 index 00000000..8eae706f --- /dev/null +++ b/contrib/non-profit-audit-reports/tests/non-profit-test-data_general-ledger.ods diff --git a/contrib/non-profit-audit-reports/unpaid-accruals-report.plx b/contrib/non-profit-audit-reports/unpaid-accruals-report.plx new file mode 100755 index 00000000..f481e02f --- /dev/null +++ b/contrib/non-profit-audit-reports/unpaid-accruals-report.plx @@ -0,0 +1,110 @@ +#!/usr/bin/perl +# unpaid-acccurals-report.plx -*- Perl -*- + +# This report is designed to create what our accounts call a "Schedule of +# accounts payable". and "Schedule of accounts receivable". + + + +# Copyright (C) 2013 Bradley M. Kuhn +# +# This program gives you software freedom; you can copy, modify, convey, +# and/or redistribute it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program in a file called 'GPLv3'. If not, write to the: +# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor +# Boston, MA 02110-1301, USA. + +use strict; +use warnings; + +use Math::BigFloat; +use Date::Manip; + +my $LEDGER_CMD = "/usr/local/bin/ledger"; + +my $ACCT_WIDTH = 70; + +sub ParseNumber($) { + $_[0] =~ s/,//g; + return Math::BigFloat->new($_[0]); +} +Math::BigFloat->precision(-2); +my $ZERO = Math::BigFloat->new("0.00"); +my $TWO_CENTS = Math::BigFloat->new("0.02"); + +if (@ARGV < 2) { + print STDERR "usage: $0 <START_DATE> <END_DATE> <LEDGER_OPTIONS>\n"; + exit 1; +} +my($startDate, $endDate, @mainLedgerOptions) = @ARGV; + +my $err; +my $formattedEndDate = UnixDate(DateCalc(ParseDate($endDate), ParseDateDelta("- 1 day"), \$err), + "%Y/%m/%d"); +die "Date calculation error on $endDate" if ($err); +my $formattedStartDate = UnixDate(ParseDate($startDate), "%Y/%m/%d"); +die "Date calculation error on $startDate" if ($err); + +my(@ledgerOptions) = (@mainLedgerOptions, + '-V', '-X', '$', '-e', $endDate, '-F', + '\"%(tag("Invoice"))\",\"%A\",\"%(date)\",\"%(payee)\",\"%22.108t\"\n', + '--limit', 'tag("Invoice") !~ /^\s*$/', 'reg'); + +my @possibleTypes = ('Accrued:Loans Receivable', 'Accrued:Accounts Payable', + 'Accrued:Accounts Receivable', 'Accrued:Expenses'); + +my %data; +foreach my $type (@possibleTypes) { + open(LEDGER_FUNDS, "-|", $LEDGER_CMD, @ledgerOptions, "/^$type/") + or die "Unable to run $LEDGER_CMD @ledgerOptions: $!"; + + while (my $line = <LEDGER_FUNDS>) { + next if $line =~ /"\<Adjustment\>"/; + die "Unable to parse output line $line from @ledgerOptions" + + unless $line =~ /^\s*"([^"]+)","([^"]+)","([^"]+)","([^"]+)","\s*\$\s*([\-\d\.\,]+)"\s*$/; + my($invoice, $account, $date, $payee, $amount) = ($1, $2, $3, $4, $5); + $amount = ParseNumber($amount); + + push(@{$data{$type}{$invoice}{entries}}, { account => $account, date => $date, payee => $payee, amount => $amount}); + $data{$type}{$invoice}{total} = $ZERO unless defined $data{$type}{$invoice}{total}; + $data{$type}{$invoice}{total} += $amount; + } + close LEDGER_FUNDS; + die "Failure on ledger command for $type: $!" unless ($? == 0); + +} +foreach my $type (keys %data) { + foreach my $invoice (keys %{$data{$type}}) { + delete $data{$type}{$invoice} if abs($data{$type}{$invoice}{total}) <= $TWO_CENTS; + } +} +foreach my $type (keys %data) { + delete $data{$type} if scalar(keys %{$data{$type}}) == 0; +} +foreach my $type (keys %data) { + print "\"SCHEDULE OF $type\"\n\"ENDING:\",\"$formattedEndDate\"\n\n", + '"DATE","PAYEE","ACCOUNT","AMOUNT","INVOICE"', "\n"; + foreach my $invoice (keys %{$data{$type}}) { + my $vals; + foreach my $vals (@{$data{$type}{$invoice}{entries}}) { + print "\"$vals->{date}\",\"$vals->{payee}\",\"$vals->{account}\",\"\$$vals->{amount}\",\"link:$invoice\"\n"; + } + } + print "pagebreak\n"; +} +############################################################################### +# +# Local variables: +# compile-command: "perl -c unpaid-accruals-report.plx" +# End: + diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index e69de29b..72a6da84 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -0,0 +1,105 @@ +# The following will be generated or updated when the 'doc' target is built: +# • user guide and man pages: if BUILD_DOCS is set +# • HTML versions of the above: if BUILD_DOCS and BUILD_WEB_DOCS are set +# • Doxygen / reference documentation: if USE_DOXYGEN is set + +######################################################################## + +if (USE_DOXYGEN) + find_package(Doxygen) + if(NOT DOXYGEN_FOUND) + message(FATAL_ERROR "Could not find doxygen. Reference documentation cannot be built.") + endif() + + configure_file(Doxyfile.in Doxyfile @ONLY) + + # see INPUT/FILE_PATTERNS in Doxyfile.in + file(GLOB doxygen_input_files ${CMAKE_SOURCE_DIR}/src/*.h) + + add_custom_command(OUTPUT html/index.html + COMMAND ${DOXYGEN_EXECUTABLE} Doxyfile + DEPENDS Doxyfile ${doxygen_input_files} + COMMENT "Building doxygen documentation") + add_custom_target(doc.doxygen DEPENDS html/index.html) +else() + add_custom_target(doc.doxygen) +endif() + +######################################################################## + +if(NOT BUILD_DOCS) + add_custom_target(doc DEPENDS doc.doxygen) + return() +endif() + +set(info_files ledger.texi ledger3.texi) + +find_program(MAKEINFO makeinfo) +find_program(TEXI2PDF texi2pdf) +find_program(MAN2HTML man2html) + +######################################################################## + +foreach(file ${info_files}) + get_filename_component(file_base ${file} NAME_WE) + if(BUILD_WEB_DOCS) + if(NOT MAKEINFO) + message(FATAL_ERROR "Could not find makeinfo. HTML version of documentation cannot be built.") + endif() + + add_custom_command(OUTPUT ${file_base}.html + COMMAND makeinfo --force --html --no-split -o ${file_base}.html ${CMAKE_CURRENT_SOURCE_DIR}/${file} + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${file} + VERBATIM) + list(APPEND ledger_doc_files ${file_base}.html) + endif(BUILD_WEB_DOCS) + + if(NOT TEXI2PDF) + mesage(WARNING "Could not find texi2pdf. PDF version of documentation will not be built.") + else() + add_custom_command(OUTPUT ${file_base}.pdf + COMMAND texi2pdf -b -q -o ${file_base}.pdf ${CMAKE_CURRENT_SOURCE_DIR}/${file} + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${file} + VERBATIM) + list(APPEND ledger_doc_files ${file_base}.pdf) + endif() +endforeach() + +######################################################################## + +if(BUILD_WEB_DOCS) + include(FindUnixCommands) + if(NOT BASH) + message(FATAL_ERROR "Could not find bash. Unable to build documentation.") + endif() + if(NOT MAN2HTML) + message(FATAL_ERROR "Could not find man2html. HTML version of man page cannot be built.") + endif() + + add_custom_command(OUTPUT ledger.1.html + COMMAND ${BASH} -c "man2html $<1:CMAKE_CURRENT_SOURCE_DIR>/ledger.1 | tail -n+3 > ledger.1.html" + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/ledger.1 + VERBATIM) + list(APPEND ledger_doc_files ledger.1.html) +endif(BUILD_WEB_DOCS) + +######################################################################## + +add_custom_target(doc DEPENDS ${ledger_doc_files} doc.doxygen) + +######################################################################## + +include(GNUInstallDirs) + +if(CMAKE_INSTALL_MANDIR) + install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/ledger.1 + DESTINATION ${CMAKE_INSTALL_MANDIR}/man1 COMPONENT doc) +endif(CMAKE_INSTALL_MANDIR) + +if(CMAKE_INSTALL_DOCDIR) + foreach(file ${info_files}) + get_filename_component(file_base ${file} NAME_WE) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${file_base}.pdf + DESTINATION ${CMAKE_INSTALL_DOCDIR} COMPONENT doc OPTIONAL) + endforeach() +endif(CMAKE_INSTALL_DOCDIR) diff --git a/doc/Doxyfile b/doc/Doxyfile.in index d59d3f82..e340d84a 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile.in @@ -1,89 +1,103 @@ -# Doxyfile 1.5.7.1 +# Doxyfile 1.8.3 # This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project +# doxygen (www.doxygen.org) for a project. # -# All text after a hash (#) is considered a comment and will be ignored +# All text after a hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] -# Values that contain spaces should be placed between quotes (" ") +# Values that contain spaces should be placed between quotes (" "). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all -# text before the first occurrence of this tag. Doxygen uses libiconv (or the -# iconv built into libc) for the transcoding. See +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 -# The PROJECT_NAME tag is a single word (or a sequence of words surrounded -# by quotes) that should identify the project. +# The PROJECT_NAME tag is a single word (or sequence of words) that should +# identify the project. Note that if you do not use Doxywizard you need +# to put quotes around the project name if it contains spaces. PROJECT_NAME = Ledger -# The PROJECT_NUMBER tag can be used to enter a project or revision number. -# This could be handy for archiving the generated documentation or +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = 3.0 -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) -# base path where the generated documentation will be put. -# If a relative path is entered, it will be relative to the location +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer +# a quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify an logo or icon that is +# included in the documentation. The maximum height of the logo should not +# exceed 55 pixels and the maximum width should not exceed 200 pixels. +# Doxygen will copy the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. -OUTPUT_DIRECTORY = %builddir%/doc +OUTPUT_DIRECTORY = @CMAKE_CURRENT_BINARY_DIR@ -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create -# 4096 sub-directories (in 2 levels) under the output directory of each output -# format and will distribute the generated files over these directories. -# Enabling this option can be useful when feeding doxygen a huge amount of -# source files, where putting all generated files in the same directory would +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO -# The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all constant output in the proper language. -# The default language is English, other supported languages are: -# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, -# Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek, -# Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages), -# Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish, -# Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, Slovene, -# Spanish, Swedish, and Ukrainian. +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English -# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will -# include brief member descriptions after the members that are listed in -# the file and class documentation (similar to JavaDoc). +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES -# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend -# the brief description of a member or function before the detailed description. -# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES -# This tag implements a quasi-intelligent brief description abbreviator -# that is used to form the text in various listings. Each string -# in this list, if found as the leading text of the brief description, will be -# stripped from the text and the result after processing the whole list, is -# used as the annotated text. Otherwise, the brief description is used as-is. -# If left blank, the following values are used ("$name" is automatically -# replaced with the name of the entity): "The $name class" "The $name widget" -# "The $name file" "is" "provides" "specifies" "contains" +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = "The $name class" \ @@ -98,130 +112,168 @@ ABBREVIATE_BRIEF = "The $name class" \ an \ the -# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# Doxygen will generate a detailed section even if there is only a brief +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = YES -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all -# inherited members of a class in the documentation of that class as if those -# members were ordinary class members. Constructors, destructors and assignment +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = YES -# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full -# path before files name in the file list and in the header files. If set +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = NO -# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag -# can be used to strip a user-defined part of the path. Stripping is -# only done if one of the specified strings matches the left-hand part of -# the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the -# path to strip. - -STRIP_FROM_PATH = %srcdir%/src/ - -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of -# the path mentioned in the documentation of a class, which tells -# the reader which header file to include in order to use a class. -# If left blank only the name of the header file containing the class -# definition is used. Otherwise one should specify the include paths that +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. Note that you specify absolute paths here, but also +# relative paths, which will be relative from the directory where doxygen is +# started. + +STRIP_FROM_PATH = @PROJECT_SOURCE_DIR@/src/ + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. -STRIP_FROM_INC_PATH = +STRIP_FROM_INC_PATH = -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter -# (but less readable) file names. This can be useful is your file systems +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful if your file system # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = YES -# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen -# will interpret the first line (until the first dot) of a JavaDoc-style -# comment as the brief description. If set to NO, the JavaDoc -# comments will behave just like regular Qt-style comments +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = YES -# If the QT_AUTOBRIEF tag is set to YES then Doxygen will -# interpret the first line (until the first dot) of a Qt-style -# comment as the brief description. If set to NO, the comments -# will behave just like regular Qt-style comments (thus requiring +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen -# treat a multi-line C++ special comment block (i.e. a block of //! or /// -# comments) as a brief description. This used to be the default behaviour. -# The new default is to treat a multi-line C++ comment block as a detailed +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO -# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented -# member inherits the documentation from any documented member that it +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce -# a new page for each member. If set to NO, the documentation of a member will +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO -# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 -# This tag can be used to specify a number of aliases that acts -# as commands in the documentation. An alias has the form "name=value". -# For example adding "sideeffect=\par Side Effects:\n" will allow you to -# put the command \sideeffect (or @sideeffect) in the documentation, which -# will result in a user-defined paragraph with heading "Side Effects:". +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. -ALIASES = +ALIASES = -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C -# sources only. Doxygen will then generate output that is more tailored for C. -# For instance, some of the names that are used will be different. The list +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding +# "class=itcl::class" will allow you to use the command class in the +# itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java -# sources only. Doxygen will then generate output that is more tailored for -# Java. For instance, namespaces will be presented as packages, qualified +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO -# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran -# sources only. Doxygen will then generate output that is more tailored for +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO -# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL -# sources. Doxygen will then generate output that is tailored for +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO -# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want -# to include (a tag file for) the STL sources as input, then you should -# set this tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. -# func(std::string) {}). This also make the inheritance and collaboration +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, +# and language is one of the parsers supported by doxygen: IDL, Java, +# Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, +# C++. For instance to make doxygen treat .inc files as Fortran files (default +# is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note +# that for custom extensions you also need to set FILE_PATTERNS otherwise the +# files are not read by doxygen. + +EXTENSION_MAPPING = + +# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all +# comments according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you +# can mix doxygen, HTML, and XML commands with Markdown formatting. +# Disable only in case of backward compatibilities issues. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented classes, +# or namespaces to their corresponding documentation. Such a link can be +# prevented in individual cases by by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also makes the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = YES @@ -231,473 +283,559 @@ BUILTIN_STL_SUPPORT = YES CPP_CLI_SUPPORT = NO -# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. -# Doxygen will parse them like normal C++ but will assume all classes use public +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO -# For Microsoft's IDL there are propget and propput attributes to indicate getter -# and setter methods for a property. Setting this option to YES (the default) -# will make doxygen to replace the get and set methods by a property in the -# documentation. This will only work if the methods are indeed getting or -# setting a simple type. If this is not the case, or you want to show the +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES (the +# default) will make doxygen replace the get and set methods by a property in +# the documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES -# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first -# member in the group (if any) for the other members of the group. By default +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO -# Set the SUBGROUPING tag to YES (the default) to allow class member groups of -# the same type (for instance a group of public functions) to be put as a -# subgroup of that type (e.g. under the Public Functions section). Set it to -# NO to prevent subgrouping. Alternatively, this can be done per class using +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES -# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum -# is documented as struct, union, or enum with the name of the typedef. So -# typedef struct TypeS {} TypeT, will appear in the documentation as a struct -# with name TypeT. When disabled the typedef will appear as a member of a file, -# namespace, or class. And the struct will be named TypeS. This can typically -# be useful for C code in case the coding convention dictates that all compound +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and +# unions are shown inside the group in which they are included (e.g. using +# @ingroup) instead of on a separate page (for HTML and Man pages) or +# section (for LaTeX and RTF). + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and +# unions with only public data fields will be shown inline in the documentation +# of the scope in which they are defined (i.e. file, namespace, or group +# documentation), provided this scope is documented. If set to NO (the default), +# structs, classes, and unions are shown on a separate page (for HTML and Man +# pages) or section (for LaTeX and RTF). + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO -# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. -# For small to medium size projects (<1000 input files) the default value is -# probably good enough. For larger projects a too small cache size can cause -# doxygen to be busy swapping symbols to and from disk most of the time -# causing a significant performance penality. -# If the system has enough physical memory increasing the cache will improve the -# performance by keeping more symbols in memory. Note that the value works on -# a logarithmic scale so increasing the size by one will rougly double the -# memory usage. The cache size is given by this formula: -# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, -# corresponding to a cache size of 2^16 = 65536 symbols +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penalty. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will roughly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols. SYMBOL_CACHE_SIZE = 0 +# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be +# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given +# their name and scope. Since this can be an expensive process and often the +# same symbol appear multiple times in the code, doxygen keeps a cache of +# pre-resolved symbols. If the cache is too small doxygen will become slower. +# If the cache is too large, memory is wasted. The cache size is given by this +# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols. + +LOOKUP_CACHE_SIZE = 0 + #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in -# documentation are documented, even if no documentation was available. -# Private class members and static file members will be hidden unless +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = YES -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO -# If the EXTRACT_STATIC tag is set to YES all static members of a file +# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# scope will be included in the documentation. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) -# defined locally in source files will be included in the documentation. +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = NO -# This flag is only useful for Objective-C code. When set to YES local -# methods, which are defined in the implementation section but not in -# the interface are included in the documentation. +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO -# If this flag is set to YES, the members of anonymous namespaces will be -# extracted and appear in the documentation as a namespace called -# 'anonymous_namespace{file}', where file will be replaced with the base -# name of the file that contains the anonymous namespace. By default -# anonymous namespace are hidden. +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespaces are hidden. EXTRACT_ANON_NSPACES = NO -# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all -# undocumented members of documented classes, files or namespaces. -# If set to NO (the default) these members will be included in the -# various overviews, but no documentation section is generated. +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO -# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. -# If set to NO (the default) these classes will be included in the various +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = YES -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all -# friend (class|struct|union) declarations. -# If set to NO (the default) these declarations will be included in the +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO -# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any -# documentation blocks found inside the body of a function. -# If set to NO (the default) these blocks will be appended to the +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO -# The INTERNAL_DOCS tag determines if documentation -# that is typed after a \internal command is included. If the tag is set -# to NO (the default) then the documentation will be excluded. +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = YES -# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate -# file names in lower-case letters. If set to YES upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = NO -# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen -# will show members with their full class and namespace scopes in the +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO -# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen -# will put a list of the files that are included by a file in the documentation +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES -# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES -# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen -# will sort the (detailed) documentation of file and class members -# alphabetically by member name. If set to NO the members will appear in +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the -# brief documentation of file, namespace and class members alphabetically -# by member name. If set to NO (the default) the members will appear in +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = YES -# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the -# hierarchy of group names into alphabetical order. If set to NO (the default) +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen +# will sort the (brief and detailed) documentation of class members so that +# constructors and destructors are listed first. If set to NO (the default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be -# sorted by fully-qualified names, including namespaces. If set to -# NO (the default), the class list will be sorted only by class name, -# not including the namespace part. +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the +# Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO -# The GENERATE_TODOLIST tag can be used to enable (YES) or -# disable (NO) the todo list. This list is created by putting \todo +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to +# do proper type resolution of all parameters of a function it will reject a +# match between the prototype and the implementation of a member function even +# if there is only one candidate or it is obvious which candidate to choose +# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen +# will still accept a match between prototype and implementation in such cases. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES -# The GENERATE_TESTLIST tag can be used to enable (YES) or -# disable (NO) the test list. This list is created by putting \test +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES -# The GENERATE_BUGLIST tag can be used to enable (YES) or -# disable (NO) the bug list. This list is created by putting \bug +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES -# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or -# disable (NO) the deprecated list. This list is created by putting +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES -# The ENABLED_SECTIONS tag can be used to enable conditional -# documentation sections, marked by \if sectionname ... \endif. +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if section-label ... \endif +# and \cond section-label ... \endcond blocks. -ENABLED_SECTIONS = +ENABLED_SECTIONS = -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines -# the initial value of a variable or define consists of for it to appear in -# the documentation. If the initializer consists of more lines than specified -# here it will be hidden. Use a value of 0 to hide initializers completely. -# The appearance of the initializer of individual variables and defines in the -# documentation can be controlled using \showinitializer or \hideinitializer +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or macro consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and macros in the +# documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated -# at the bottom of the documentation of classes and structs. If set to YES the +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES -# If the sources in your project are distributed over multiple directories -# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy -# in the documentation. The default is NO. - -SHOW_DIRECTORIES = NO - # Set the SHOW_FILES tag to NO to disable the generation of the Files page. -# This will remove the Files entry from the Quick Index and from the +# This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES -# Set the SHOW_NAMESPACES tag to NO to disable the generation of the -# Namespaces page. This will remove the Namespaces entry from the Quick Index +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. +# This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES -# The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from -# the version control system). Doxygen will invoke the program by executing (via -# popen()) the command <command> <input-file>, where <command> is the value of -# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file -# provided by doxygen. Whatever the program writes to standard output +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command <command> <input-file>, where <command> is the value of +# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file +# provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. -FILE_VERSION_FILTER = +FILE_VERSION_FILTER = -# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by -# doxygen. The layout file controls the global structure of the generated output files -# in an output format independent way. The create the layout file that represents -# doxygen's defaults, run doxygen with the -l option. You can optionally specify a -# file name after the option, if omitted DoxygenLayout.xml will be used as the name -# of the layout file. +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. +# You can optionally specify a file name after the option, if omitted +# DoxygenLayout.xml will be used as the name of the layout file. -LAYOUT_FILE = +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files +# containing the references data. This must be a list of .bib files. The +# .bib extension is automatically appended if omitted. Using this command +# requires the bibtex tool to be installed. See also +# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style +# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this +# feature you need bibtex and perl available in the search path. Do not use +# file names with spaces, bibtex cannot handle them. + +CITE_BIB_FILES = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- -# The QUIET tag can be used to turn on/off the messages that are generated +# The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO -# The WARNINGS tag can be used to turn on/off the warning messages that are -# generated by doxygen. Possible values are YES and NO. If left blank +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES -# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings -# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. # jww (2009-01-31): Enable this toward the end -#WARN_IF_UNDOCUMENTED = YES WARN_IF_UNDOCUMENTED = NO -# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some -# parameters in a documented function, or documenting parameters that +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES -# This WARN_NO_PARAMDOC option can be abled to get warnings for -# functions that are documented, but have no documentation for their parameters -# or return value. If set to NO (the default) doxygen will only warn about -# wrong or incomplete parameter documentation, but not about the absence of +# The WARN_NO_PARAMDOC option can be enabled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = YES -# The WARN_FORMAT tag determines the format of the warning messages that -# doxygen can produce. The string should contain the $file, $line, and $text -# tags, which will be replaced by the file and line number from which the -# warning originated and the warning text. Optionally the format may contain -# $version, which will be replaced by the version of the file (if it could +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" -# The WARN_LOGFILE tag can be used to specify a file to which warning -# and error messages should be written. If left blank the output is written +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written # to stderr. -WARN_LOGFILE = +WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- -# The INPUT tag can be used to specify the files and/or directories that contain -# documented source files. You may enter file names like "myfile.cpp" or -# directories like "/usr/src/myproject". Separate the files or directories +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories # with spaces. -INPUT = src +# please update dependencies in CMakeList.txt if you change this +INPUT = @PROJECT_SOURCE_DIR@/src -# This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is -# also the default input encoding. Doxygen uses libiconv (or the iconv built -# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 -# If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank the following patterns are tested: -# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx -# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh +# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py +# *.f90 *.f *.for *.vhd *.vhdl +# please update dependencies in CMakeList.txt if you change this FILE_PATTERNS = *.h -# The RECURSIVE tag can be used to turn specify whether or not subdirectories -# should be searched for input files as well. Possible values are YES and NO. +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = NO -# The EXCLUDE tag can be used to specify files and/or directories that should -# excluded from the INPUT source files. This way you can easily exclude a +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. +# Note that relative paths are relative to the directory from which doxygen is +# run. -EXCLUDE = +EXCLUDE = -# The EXCLUDE_SYMLINKS tag can be used select whether or not files or -# directories that are symbolic links (a Unix filesystem feature) are excluded +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO -# If the value of the INPUT tag contains directories, you can use the -# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. Note that the wildcards are matched -# against the file with absolute path, so to exclude all test directories +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* -EXCLUDE_PATTERNS = +EXCLUDE_PATTERNS = -# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names -# (namespaces, classes, functions, etc.) that should be excluded from the -# output. The symbol name can be a fully qualified name, a word, or if the -# wildcard * is used, a substring. Examples: ANamespace, AClass, +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test -EXCLUDE_SYMBOLS = +EXCLUDE_SYMBOLS = -# The EXAMPLE_PATH tag can be used to specify one or more files or -# directories that contain example code fragments that are included (see +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see # the \include command). -EXAMPLE_PATH = +EXAMPLE_PATH = -# If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = * -# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude -# commands irrespective of the value of the RECURSIVE tag. +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO -# The IMAGE_PATH tag can be used to specify one or more files or -# directories that contain image that are included in the documentation (see +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see # the \image command). -IMAGE_PATH = +IMAGE_PATH = -# The INPUT_FILTER tag can be used to specify a program that doxygen should -# invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command <filter> <input-file>, where <filter> -# is the value of the INPUT_FILTER tag, and <input-file> is the name of an -# input file. Doxygen will then use the output that the filter program writes -# to standard output. If FILTER_PATTERNS is specified, this tag will be +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command <filter> <input-file>, where <filter> +# is the value of the INPUT_FILTER tag, and <input-file> is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. +# If FILTER_PATTERNS is specified, this tag will be # ignored. -INPUT_FILTER = +INPUT_FILTER = -# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. The filters are a list of the form: -# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further -# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER -# is applied to all files. +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. +# Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. +# The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty or if +# non of the patterns match the file name, INPUT_FILTER is applied. -FILTER_PATTERNS = +FILTER_PATTERNS = -# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER) will be used to filter the input files when producing source +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) +# and it is also possible to disable source filtering for a specific pattern +# using *.ext= (so without naming a filter). This option only has effect when +# FILTER_SOURCE_FILES is enabled. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page (index.html). +# This can be useful if you have a project on for instance GitHub and want reuse +# the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- -# If the SOURCE_BROWSER tag is set to YES then a list of source files will -# be generated. Documented entities will be cross-referenced with these sources. -# Note: To get rid of all source code in the generated output, make sure also +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = YES -# Setting the INLINE_SOURCES tag to YES will include the body +# Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO -# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct -# doxygen to hide any special comment blocks from generated source code -# fragments. Normal C and C++ comments will always remain visible. +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C, C++ and Fortran comments will always remain visible. STRIP_CODE_COMMENTS = YES -# If the REFERENCED_BY_RELATION tag is set to YES -# then for each documented function all documented +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES -# If the REFERENCES_RELATION tag is set to YES -# then for each documented function all documented entities +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES @@ -705,20 +843,21 @@ REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will -# link to the source code. Otherwise they will link to the documentstion. +# link to the source code. +# Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES -# If the USE_HTAGS tag is set to YES then the references to source code -# will point to the HTML generated by the htags(1) tool instead of doxygen -# built-in source browser. The htags tool is part of GNU's global source -# tagging system (see http://www.gnu.org/software/global/global.html). You +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO -# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen -# will generate a verbatim copy of the header file for each class for +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES @@ -727,130 +866,201 @@ VERBATIM_HEADERS = YES # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index -# of all compounds will be generated. Enable this if the project +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = YES -# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then -# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 -# In case all classes in a project start with a common prefix, all -# classes will be put under the same header in the alphabetical index. -# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. -IGNORE_PREFIX = +IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- -# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for -# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html -# The HTML_HEADER tag can be used to specify a personal HTML header for -# each generated HTML page. If it is left blank doxygen will generate a -# standard header. - -HTML_HEADER = - -# The HTML_FOOTER tag can be used to specify a personal HTML footer for -# each generated HTML page. If it is left blank doxygen will generate a +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. Note that when using a custom header you are responsible +# for the proper inclusion of any scripts and style sheets that doxygen +# needs, which is dependent on the configuration options used. +# It is advised to generate a default header using "doxygen -w html +# header.html footer.html stylesheet.css YourConfigFile" and then modify +# that header. Note that the header is subject to change so you typically +# have to redo this when upgrading to a newer version of doxygen or when +# changing the value of configuration settings such as GENERATE_TREEVIEW! + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a # standard footer. -HTML_FOOTER = - -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading -# style sheet that is used by each HTML page. It can be used to -# fine-tune the look of the HTML output. If the tag is left blank doxygen -# will generate a default style sheet. Note that doxygen will try to copy -# the style sheet file to the HTML output directory, so don't put your own -# stylesheet in the HTML output directory as well, or it will be erased! - -HTML_STYLESHEET = - -# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, -# files or namespaces will be aligned in HTML using tables. If set to -# NO a bullet list will be used. - -HTML_ALIGN_MEMBERS = YES - -# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML -# documentation will contain sections that can be hidden and shown after the -# page has loaded. For this to work a browser that supports -# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox -# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). - -HTML_DYNAMIC_SECTIONS = NO - -# If the GENERATE_DOCSET tag is set to YES, additional index files -# will be generated that can be used as input for Apple's Xcode 3 -# integrated development environment, introduced with OSX 10.5 (Leopard). -# To create a documentation set, doxygen will generate a Makefile in the -# HTML output directory. Running make will produce the docset in that -# directory and running "make install" will install the docset in -# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find -# it at startup. -# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information. +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If left blank doxygen will +# generate a default style sheet. Note that it is recommended to use +# HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this +# tag will in the future become obsolete. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional +# user-defined cascading style sheet that is included after the standard +# style sheets created by doxygen. Using this option one can overrule +# certain style aspects. This is preferred over using HTML_STYLESHEET +# since it does not replace the standard style sheet and is therefor more +# robust against future updates. Doxygen will copy the style sheet file to +# the output directory. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that +# the files will be copied as-is; there are no commands or markers available. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. +# Doxygen will adjust the colors in the style sheet and background images +# according to this color. Hue is specified as an angle on a colorwheel, +# see http://en.wikipedia.org/wiki/Hue for more information. +# For instance the value 0 represents red, 60 is yellow, 120 is green, +# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. +# The allowed range is 0 to 359. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of +# the colors in the HTML output. For a value of 0 the output will use +# grayscales only. A value of 255 will produce the most vivid colors. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to +# the luminance component of the colors in the HTML output. Values below +# 100 gradually make the output lighter, whereas values above 100 make +# the output darker. The value divided by 100 is the actual gamma applied, +# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, +# and 100 does not change the gamma. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting +# this to NO can help when comparing the output of multiple runs. + +HTML_TIMESTAMP = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. + +HTML_DYNAMIC_SECTIONS = YES + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of +# entries shown in the various tree structured indices initially; the user +# can expand and collapse entries dynamically later on. Doxygen will expand +# the tree to such a level that at most the specified number of entries are +# visible (unless a fully collapsed tree already exceeds this amount). +# So setting the number of entries 1 will produce a full collapsed tree by +# default. 0 is a special value representing an infinite number of entries +# and will result in a full expanded tree by default. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. GENERATE_DOCSET = NO -# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the -# feed. A documentation feed provides an umbrella under which multiple -# documentation sets from a single provider (such as a company or product suite) +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" -# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that -# should uniquely identify the documentation set bundle. This should be a -# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project -# If the GENERATE_HTMLHELP tag is set to YES, additional index files -# will be generated that can be used as input for tools like the -# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely +# identify the documentation publisher. This should be a reverse domain-name +# style string, e.g. com.mycompany.MyDocSet.documentation. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can -# be used to specify the file name of the resulting .chm file. You -# can add a path in front of the file if the result should not be +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be # written to the html output directory. -CHM_FILE = +CHM_FILE = -# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can -# be used to specify the location (absolute path including file name) of -# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. -HHC_LOCATION = +HHC_LOCATION = -# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag -# controls if a separate .chi index file is generated (YES) or that +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO @@ -859,235 +1069,396 @@ GENERATE_CHI = NO # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. -CHM_INDEX_ENCODING = +CHM_INDEX_ENCODING = -# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag -# controls whether a binary table of contents is generated (YES) or a +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO -# The TOC_EXPAND flag can be set to YES to add extra items for group members +# The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO -# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER -# are set, an additional index file will be generated that can be used as input for -# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated -# HTML documentation. +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated +# that can be used as input for Qt's qhelpgenerator to generate a +# Qt Compressed Help (.qch) of the generated HTML documentation. GENERATE_QHP = NO -# If the QHG_LOCATION tag is specified, the QCH_FILE tag can -# be used to specify the file name of the resulting .qch file. +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. -QCH_FILE = +QCH_FILE = -# The QHP_NAMESPACE tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# <a href="http://doc.trolltech.com/qthelpproject.html#namespace">Qt Help Project / Namespace</a>. +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project -# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# <a href="http://doc.trolltech.com/qthelpproject.html#virtual-folders">Qt Help Project / Virtual Folders</a>. +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc -# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can -# be used to specify the location of Qt's qhelpgenerator. -# If non-empty doxygen will try to run qhelpgenerator on the generated -# .qhp file . +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to +# add. For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters -QHG_LOCATION = +QHP_CUST_FILTER_NAME = -# The DISABLE_INDEX tag can be used to turn on/off the condensed index at -# top of each HTML page. The value NO (the default) enables the index and -# the value YES disables it. +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see +# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters"> +# Qt Help Project / Custom Filters</a>. -DISABLE_INDEX = NO +QHP_CUST_FILTER_ATTRS = -# This tag can be used to set the number of enum values (range [1..20]) -# that doxygen will group on one line in the generated HTML documentation. +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes"> +# Qt Help Project / Filter Attributes</a>. -ENUM_VALUES_PER_LINE = 4 +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before +# the help appears. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) +# at top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. Since the tabs have the same information as the +# navigation tree you can set this option to NO if you already set +# GENERATE_TREEVIEW to YES. + +DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. -# If the tag value is set to FRAME, a side panel will be generated -# containing a tree-like index structure (just like the one that -# is generated for HTML Help). For this to work a browser that supports -# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, -# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are -# probably better off using the HTML help feature. Other possible values -# for this tag are: HIERARCHIES, which will generate the Groups, Directories, -# and Class Hierarchy pages using a tree view instead of an ordered list; -# ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which -# disables this behavior completely. For backwards compatibility with previous -# releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE -# respectively. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. +# Since the tree basically has the same information as the tab index you +# could consider to set DISABLE_INDEX to NO when enabling this option. GENERATE_TREEVIEW = YES -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be -# used to set the initial width (in pixels) of the frame in which the tree +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values +# (range [0,1..20]) that doxygen will group on one line in the generated HTML +# documentation. Note that a value of 0 will completely suppress the enum +# values from appearing in the overview section. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 -# Use this tag to change the font size of Latex formulas included -# as images in the HTML documentation. The default is 10. Note that -# when you change the font size after a successful doxygen run you need -# to manually remove any form_*.png images from the HTML output directory +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open +# links to external symbols imported via tag files in a separate window. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are +# not supported properly for IE 6.0, but are supported on all modern browsers. +# Note that when changing this option you need to delete any form_*.png files +# in the HTML output before the changes have effect. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax +# (see http://www.mathjax.org) which uses client side Javascript for the +# rendering instead of using prerendered bitmaps. Use this if you do not +# have LaTeX installed or if you want to formulas look prettier in the HTML +# output. When enabled you may also need to install MathJax separately and +# configure the path to it using the MATHJAX_RELPATH option. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# thA MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and +# SVG. The default value is HTML-CSS, which is slower, but has the best +# compatibility. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the +# HTML output directory using the MATHJAX_RELPATH option. The destination +# directory should contain the MathJax.js script. For instance, if the mathjax +# directory is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to +# the MathJax Content Delivery Network so you can quickly see the result without +# installing MathJax. +# However, it is strongly recommended to install a local +# copy of MathJax from http://www.mathjax.org before deployment. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension +# names that should be enabled during MathJax rendering. + +MATHJAX_EXTENSIONS = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box +# for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a web server instead of a web client using Javascript. +# There are two flavours of web server based search depending on the +# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for +# searching and an index file used by the script. When EXTERNAL_SEARCH is +# enabled the indexing and searching needs to be provided by external tools. +# See the manual for details. + +SERVER_BASED_SEARCH = NO + +# When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP +# script for searching. Instead the search results are written to an XML file +# which needs to be processed by an external indexer. Doxygen will invoke an +# external search engine pointed to by the SEARCHENGINE_URL option to obtain +# the search results. Doxygen ships with an example indexer (doxyindexer) and +# search engine (doxysearch.cgi) which are based on the open source search engine +# library Xapian. See the manual for configuration details. + +EXTERNAL_SEARCH = NO + +# The SEARCHENGINE_URL should point to a search engine hosted by a web server +# which will returned the search results when EXTERNAL_SEARCH is enabled. +# Doxygen ships with an example search engine (doxysearch) which is based on +# the open source search engine library Xapian. See the manual for configuration +# details. + +SEARCHENGINE_URL = + +# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed +# search data is written to a file for indexing by an external tool. With the +# SEARCHDATA_FILE tag the name of this file can be specified. + +SEARCHDATA_FILE = searchdata.xml + +# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through other +# doxygen projects that are not otherwise connected via tags files, but are +# all added to the same search index. Each project needs to have a tag file set +# via GENERATE_TAGFILE. The search mapping then maps the name of the tag file +# to a relative location where the documentation can be found, +# similar to the +# TAGFILES option but without actually processing the tag file. +# The format is: EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ... + +EXTRA_SEARCH_MAPPINGS = + #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- -# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = YES -# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex -# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. LATEX_CMD_NAME = latex -# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to -# generate index for LaTeX. If left blank `makeindex' will be used as the +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex -# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact -# LaTeX documents. This may be useful for small projects and may help to +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO -# The PAPER_TYPE tag can be used to set the paper type that is used -# by the printer. Possible values are: a4, a4wide, letter, legal and +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = letter -# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. -EXTRA_PACKAGES = +EXTRA_PACKAGES = -# The LATEX_HEADER tag can be used to specify a personal LaTeX header for -# the generated latex document. The header should contain everything until -# the first chapter. If it is left blank doxygen will generate a +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! -LATEX_HEADER = +LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for +# the generated latex document. The footer should contain everything after +# the last chapter. If it is left blank doxygen will generate a +# standard footer. Notice: only use this tag if you know what you are doing! -# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated -# is prepared for conversion to pdf (using ps2pdf). The pdf file will -# contain links (just like the HTML output) instead of page references +LATEX_FOOTER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES -# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of -# plain latex in the generated Makefile. Set this option to YES to get a +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = YES -# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. -# command to the generated LaTeX files. This will instruct LaTeX to keep -# running if errors occur, instead of asking the user for help. +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO -# If LATEX_HIDE_INDICES is set to YES then doxygen will not -# include the index chapters (such as File Index, Compound Index, etc.) +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings +# such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +# The LATEX_BIB_STYLE tag can be used to specify the style to use for the +# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See +# http://en.wikipedia.org/wiki/BibTeX for more info. + +LATEX_BIB_STYLE = plain + #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- -# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output -# The RTF output is optimized for Word 97 and may not look very pretty with +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO -# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf -# If the COMPACT_RTF tag is set to YES Doxygen generates more compact -# RTF documents. This may be useful for small projects and may help to +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO -# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated -# will contain hyperlink fields. The RTF file will -# contain links (just like the HTML output) instead of page references. -# This makes the output suitable for online browsing using WORD or other -# programs which support those fields. +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO -# Load stylesheet definitions from file. Syntax is similar to doxygen's -# config file, i.e. a series of assignments. You only have to provide +# Load style sheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. -RTF_STYLESHEET_FILE = +RTF_STYLESHEET_FILE = -# Set optional variables used in the generation of an rtf document. +# Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. -RTF_EXTENSIONS_FILE = +RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- -# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO -# The MAN_OUTPUT tag is used to specify where the man pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man -# The MAN_EXTENSION tag determines the extension that is added to +# The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 -# If the MAN_LINKS tag is set to YES and Doxygen generates man output, -# then it will generate one additional man file for each entity -# documented in the real man page(s). These additional files -# only source the real man page, but without them the man command +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO @@ -1096,33 +1467,33 @@ MAN_LINKS = NO # configuration options related to the XML output #--------------------------------------------------------------------------- -# If the GENERATE_XML tag is set to YES Doxygen will -# generate an XML file that captures the structure of +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO -# The XML_OUTPUT tag is used to specify where the XML pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml -# The XML_SCHEMA tag can be used to specify an XML schema, -# which can be used by a validating XML parser to check the +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the # syntax of the XML files. -XML_SCHEMA = +XML_SCHEMA = -# The XML_DTD tag can be used to specify an XML DTD, -# which can be used by a validating XML parser to check the +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the # syntax of the XML files. -XML_DTD = +XML_DTD = -# If the XML_PROGRAMLISTING tag is set to YES Doxygen will -# dump the program listings (including syntax highlighting -# and cross-referencing information) to the XML output. Note that +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES @@ -1131,10 +1502,10 @@ XML_PROGRAMLISTING = YES # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- -# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will -# generate an AutoGen Definitions (see autogen.sf.net) file -# that captures the structure of the code including all -# documentation. Note that this feature is still experimental +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO @@ -1143,343 +1514,364 @@ GENERATE_AUTOGEN_DEF = NO # configuration options related to the Perl module output #--------------------------------------------------------------------------- -# If the GENERATE_PERLMOD tag is set to YES Doxygen will -# generate a Perl module file that captures the structure of -# the code including all documentation. Note that this -# feature is still experimental and incomplete at the +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO -# If the PERLMOD_LATEX tag is set to YES Doxygen will generate -# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO -# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be -# nicely formatted so it can be parsed by a human reader. This is useful -# if you want to understand what is going on. On the other hand, if this -# tag is set to NO the size of the Perl module output will be much smaller +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. +# This is useful +# if you want to understand what is going on. +# On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES -# The names of the make variables in the generated doxyrules.make file -# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. -# This is useful so different doxyrules.make files included by the same +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. -PERLMOD_MAKEVAR_PREFIX = +PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- -# Configuration options related to the preprocessor +# Configuration options related to the preprocessor #--------------------------------------------------------------------------- -# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will -# evaluate all C-preprocessor directives found in the sources and include +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES -# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro -# names in the source code. If set to NO (the default) only conditional -# compilation will be performed. Macro expansion can be done in a controlled +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = YES -# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES -# then the macro expansion is limited to the macros specified with the +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO -# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files -# in the INCLUDE_PATH (see below) will be search if a #include is found. +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# pointed to by INCLUDE_PATH will be searched when a #include is found. SEARCH_INCLUDES = YES -# The INCLUDE_PATH tag can be used to specify one or more directories that -# contain include files that are not input files but should be processed by +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by # the preprocessor. -INCLUDE_PATH = +INCLUDE_PATH = -# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard -# patterns (like *.h and *.hpp) to filter out the header-files in the -# directories. If left blank, the patterns specified with FILE_PATTERNS will +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. -INCLUDE_FILE_PATTERNS = +INCLUDE_FILE_PATTERNS = -# The PREDEFINED tag can be used to specify one or more macro names that -# are defined before the preprocessor is started (similar to the -D option of -# gcc). The argument of the tag is a list of macros of the form: name -# or name=definition (no spaces). If the definition and the = are -# omitted =1 is assumed. To prevent a macro definition from being -# undefined via #undef or recursively expanded use the := operator +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator # instead of the = operator. -PREDEFINED = +PREDEFINED = -# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then -# this tag can be used to specify a list of macro names that should be expanded. -# The macro definition that is found in the sources will be used. -# Use the PREDEFINED tag if you want to use a different macro definition. +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition that +# overrules the definition found in the source code. -EXPAND_AS_DEFINED = +EXPAND_AS_DEFINED = -# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then -# doxygen's preprocessor will remove all function-like macros that are alone -# on a line, have an all uppercase name, and do not end with a semicolon. Such -# function macros are typically used for boiler-plate code, and will confuse -# the parser if not removed. +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all references to function-like macros +# that are alone on a line, have an all uppercase name, and do not end with a +# semicolon, because these will confuse the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- -# Configuration::additions related to external references +# Configuration::additions related to external references #--------------------------------------------------------------------------- -# The TAGFILES option can be used to specify one or more tagfiles. -# Optionally an initial location of the external documentation -# can be added for each tagfile. The format of a tag file without -# this location is as follows: -# TAGFILES = file1 file2 ... -# Adding location for the tag files is done as follows: -# TAGFILES = file1=loc1 "file2 = loc2" ... -# where "loc1" and "loc2" can be relative or absolute paths or -# URLs. If a location is present for each tag, the installdox tool -# does not have to be run to correct the links. -# Note that each tag file must have a unique name -# (where the name does NOT include the path) -# If a tag file is not located in the directory in which doxygen -# is run, you must also specify the path to the tagfile here. - -TAGFILES = - -# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# The TAGFILES option can be used to specify one or more tagfiles. For each +# tag file the location of the external documentation should be added. The +# format of a tag file without this location is as follows: +# +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths +# or URLs. Note that each tag file must have a unique name (where the name does +# NOT include the path). If a tag file is not located in the directory in which +# doxygen is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. -GENERATE_TAGFILE = +GENERATE_TAGFILE = -# If the ALLEXTERNALS tag is set to YES all external classes will be listed -# in the class index. If set to NO only the inherited external classes +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO -# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed -# in the modules index. If set to NO, only the current project's groups will +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES -# The PERL_PATH should be the absolute path and name of the perl script +# The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- -# Configuration options related to the dot tool +# Configuration options related to the dot tool #--------------------------------------------------------------------------- -# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will -# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base -# or super classes. Setting the tag to NO turns the diagrams off. Note that -# this option is superseded by the HAVE_DOT option below. This is only a -# fallback. It is recommended to install and use dot, since it yields more -# powerful graphs. +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option also works with HAVE_DOT disabled, but it is recommended to +# install and use dot, since it yields more powerful graphs. CLASS_DIAGRAMS = YES -# You can define message sequence charts within doxygen comments using the \msc -# command. Doxygen will then run the mscgen tool (see -# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the -# documentation. The MSCGEN_PATH tag allows you to specify the directory where -# the mscgen tool resides. If left empty the tool is assumed to be found in the +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. -MSCGEN_PATH = +MSCGEN_PATH = -# If set to YES, the inheritance and collaboration graphs will hide -# inheritance and usage relations if the target is undocumented +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES -# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is -# available from the path. This tool is part of Graphviz, a graph visualization -# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = YES -# By default doxygen will write a font called FreeSans.ttf to the output -# directory and reference it in all dot files that doxygen generates. This -# font does not include all possible unicode characters however, so when you need -# these (or just want a differently looking font) you can specify the font name -# using DOT_FONTNAME. You need need to make sure dot is able to find the font, -# which can be done by putting it in a standard location or by setting the -# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory -# containing the font. +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is +# allowed to run in parallel. When set to 0 (the default) doxygen will +# base this on the number of processors available in the system. You can set it +# explicitly to a value larger than 0 to get control over the balance +# between CPU load and processing speed. + +DOT_NUM_THREADS = 0 + +# By default doxygen will use the Helvetica font for all dot files that +# doxygen generates. When you want a differently looking font you can specify +# the font name using DOT_FONTNAME. You need to make sure dot is able to find +# the font, which can be done by putting it in a standard location or by setting +# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the +# directory containing the font. DOT_FONTNAME = FreeSans -# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 11 -# By default doxygen will tell dot to use the output directory to look for the -# FreeSans.ttf font (which doxygen will put there itself). If you specify a -# different font using DOT_FONTNAME you can set the path where dot -# can find it using this tag. +# By default doxygen will tell dot to use the Helvetica font. +# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to +# set the path where dot can find it. -DOT_FONTPATH = +DOT_FONTPATH = -# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect inheritance relations. Setting this tag to YES will force the -# the CLASS_DIAGRAMS tag to NO. +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES -# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect implementation dependencies (inheritance, containment, and +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES -# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES -# If the UML_LOOK tag is set to YES doxygen will generate inheritance and -# collaboration diagrams in a style similar to the OMG's Unified Modeling +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO -# If set to YES, the inheritance and collaboration graphs will show the +# If the UML_LOOK tag is enabled, the fields and methods are shown inside +# the class node. If there are many fields or methods and many nodes the +# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS +# threshold limits the number of items for each type to make the size more +# managable. Set this to 0 for no limit. Note that the threshold may be +# exceeded by 50% before the limit is enforced. + +UML_LIMIT_NUM_FIELDS = 10 + +# If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = YES -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT -# tags are set to YES then doxygen will generate a graph for each documented -# file showing the direct and indirect include dependencies of the file with +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and -# HAVE_DOT tags are set to YES then doxygen will generate a graph for each -# documented header file showing the documented files that directly or +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES -# If the CALL_GRAPH and HAVE_DOT options are set to YES then -# doxygen will generate a call dependency graph for every global function -# or class method. Note that enabling this option will significantly increase -# the time of a run. So in most cases it will be better to enable call graphs +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = YES -# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then -# doxygen will generate a caller dependency graph for every global function -# or class method. Note that enabling this option will significantly increase -# the time of a run. So in most cases it will be better to enable caller +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = YES -# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen -# will graphical hierarchy of all classes instead of a textual one. +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will generate a graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES -# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES -# then doxygen will show the dependencies a directory has on other directories +# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES -# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images -# generated by dot. Possible values are png, jpg, or gif -# If left blank png will be used. +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are svg, png, jpg, or gif. +# If left blank png will be used. If you choose svg you need to set +# HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible in IE 9+ (other browsers do not have this requirement). DOT_IMAGE_FORMAT = jpg -# The tag DOT_PATH can be used to specify the path where the dot tool can be +# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to +# enable generation of interactive SVG images that allow zooming and panning. +# Note that this requires a modern browser other than Internet Explorer. +# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you +# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible. Older versions of IE do not have SVG support. + +INTERACTIVE_SVG = NO + +# The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. -DOT_PATH = +DOT_PATH = -# The DOTFILE_DIRS tag can be used to specify one or more directories that -# contain dot files that are included in the documentation (see the +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the # \dotfile command). -DOTFILE_DIRS = +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the +# \mscfile command). + +MSCFILE_DIRS = -# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of -# nodes that will be shown in the graph. If the number of nodes in a graph -# becomes larger than this value, doxygen will truncate the graph, which is -# visualized by representing a node as a red box. Note that doxygen if the -# number of direct children of the root node in a graph is already larger than -# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 -# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the -# graphs generated by dot. A depth value of 3 means that only nodes reachable -# from the root by following a path via at most 3 edges will be shown. Nodes -# that lay further from the root node will be omitted. Note that setting this -# option to 1 or 2 may greatly reduce the computation time needed for large -# code bases. Also note that the size of a graph can be further restricted by +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 1000 -# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent -# background. This is disabled by default, because dot on Windows does not -# seem to support this out of the box. Warning: Depending on the platform used, -# enabling this option may lead to badly anti-aliased labels on the edges of +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO -# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output -# files in one run (i.e. multiple -o and -T options on the command line). This -# makes dot run faster, but since only newer versions of dot (>1.8.10) +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO -# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will -# generate a legend page explaining the meaning of the various boxes and +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES -# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will -# remove the intermediate dot files that are used to generate +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES - -#--------------------------------------------------------------------------- -# Configuration::additions related to the search engine -#--------------------------------------------------------------------------- - -# The SEARCHENGINE tag specifies whether or not a search engine should be -# used. If set to NO the values of all tags below this one will be ignored. - -SEARCHENGINE = NO diff --git a/doc/Makefile b/doc/Makefile deleted file mode 100644 index 64a271fa..00000000 --- a/doc/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -# quick doc-building makefile used by website -# requires: man2html, texinfo - -docs: ledger.1.html ledger.html ledger.pdf ledger3.html ledger3.pdf - -%.1.html: %.1 - -man2html $< | tail -n+3 >$@ - -%.html: %.texi - -makeinfo --force --html --no-split -o $@ $< - -%.pdf: %.texi - -texi2pdf -b -q $< - rm -f $*.aux $*.cp $*.fn $*.ky $*.log $*.pg $*.toc $*.tp $*.vr - -clean: - rm -rf ledger.1.html ledger.html ledger3.html ledger.pdf ledger3.pdf diff --git a/doc/ledger.1 b/doc/ledger.1 index b96e4a7d..659d3fbb 100644 --- a/doc/ledger.1 +++ b/doc/ledger.1 @@ -92,29 +92,23 @@ Report of postings matching the .Ar report-query in CSV format (comma-separated values). Useful for exporting data to a spreadsheet for further analysis or charting. -.It Nm draft Oo Ar draft-template Oc +.It Nm entry Oo Ar entry-template Oc Generate and display a new, properly formatted Ledger transaction by comparing the -.Ar draft-template +.Ar entry-template to the transactions in your data file(s). For more information on draft templates and using this command to quickly create new transactions, see the section -.Sx DRAFTS . +.Sx ENTRIES . .Pp -The synonyms -.Nm entry -and +The synonym .Nm xact -are also accepted. +is also accepted. .It Nm emacs Oo Ar query Oc Outputs posting and transaction data in a format readily consumed by the Emacs editor, in a series of Lisp forms. This is used by the .Li ledger.el Emacs mode to process reporting data from Ledger. -.Pp -The synonym -.Nm lisp -is also accepted. .It Nm equity Oo Ar report-query Oc Prints a series of transactions that balance current totals for accounts matching the @@ -586,9 +580,9 @@ for example: .It Nm xact .El .Pp -.Sh DRAFTS +.Sh ENTRIES .Pp -.Sh FORMATS +.Sh FORMATS .Pp .Sh DEBUG COMMANDS In addition to the regular reporting commands, Ledger also accepts several diff --git a/doc/ledger3.texi b/doc/ledger3.texi index 1ea4fd02..f60bb26e 100644 --- a/doc/ledger3.texi +++ b/doc/ledger3.texi @@ -235,8 +235,8 @@ $ ledger -f ledger.dat register Bell @end smallexample An important difference between Ledger and other finance packages is -that journal will never alter your input file. You can create and edit -that file in any way you prefer, but journal is only for analyzing the +that Ledger will never alter your input file. You can create and edit +that file in any way you prefer, but Ledger is only for analyzing the data, not for altering it. @@ -261,7 +261,7 @@ enter these commands: Ledger has a complete online help system based on GNU Info. This manual can be searched directly from the command line using the following options: -@option{ledger --help} bring up this entire manual in your tty. +@code{ledger --help} bring up this entire manual in your tty. If you need help on how to use Ledger, or run into problems, you can join the Ledger mailing list at the following Web address: @@ -270,8 +270,8 @@ join the Ledger mailing list at the following Web address: http://groups.google.com/group/ledger-cli @end smallexample -@noindent You can also find help at the @samp{#ledger} channel on the IRC server -@samp{irc.freenode.net}. +@noindent You can also find help at the @code{#ledger} channel on the IRC server +@code{irc.freenode.net}. @cindex tutorial @node Ledger Tutorial , Principles of Accounting, Introduction to Ledger, Top @@ -280,7 +280,6 @@ http://groups.google.com/group/ledger-cli @menu * Start a Journal:: * Run Some Reports:: -* Command Line Quick Reference:: @end menu @node Start a Journal, Run Some Reports, Ledger Tutorial , Ledger Tutorial @@ -293,10 +292,9 @@ called @file{drewr3.dat} (@pxref{Example Data File}). Copy it someplace convenient and open up a terminal window in that directory. -If you would rather start with your own journal right away please skip -to @xref{Keeping a Journal}. +If you would rather start with your own journal right away please see @ref{Keeping a Journal}. -@node Run Some Reports, Command Line Quick Reference, Start a Journal, Ledger Tutorial +@node Run Some Reports, , Start a Journal, Ledger Tutorial @section Run a Few Reports @menu @@ -370,11 +368,11 @@ To show all transactions and a running total: ledger -f drewr3.dat register @end smallexample -Ledger will generate: +@noindent Ledger will generate: @smallexample 10-Dec-01 Checking balance Assets:Checking $ 1,000.00 $ 1,000.00 - Equity:Opening Balances $ -1,000.00 0 + Equity:Opening Balances $ -1,000.00 0 10-Dec-20 Organic Co-op Expense:Food:Groceries $ 37.50 $ 37.50 Expense:Food:Groceries $ 37.50 $ 75.00 Expense:Food:Groceries $ 37.50 $ 112.50 @@ -421,14 +419,14 @@ $ ledger -f drewr3.dat register Groceries 11-Jan-19 Grocery Store Expense:Food:Groceries $ 44.00 $ 334.00 @end smallexample -@noindent Which matches the balance reported for the @samp{Groceries} account: +@noindent Which matches the balance reported for the @code{Groceries} account: @smallexample $ ledger -f drewr3.dat balance Groceries $ 334.00 Expenses:Food:Groceries @end smallexample -@noindent If you would like to find transaction to only a certain payee use @samp{payee} or @@: +@noindent If you would like to find transaction to only a certain payee use @code{payee} or @@: @smallexample $ ledger -f drewr3.dat register payee "Organic" 10-Dec-20 Organic Co-op Expense:Food:Groceries $ 37.50 $ 37.50 @@ -447,7 +445,8 @@ $ ledger -f drewr3.dat register payee "Organic" A very useful report is to show what your obligations are versus what expenditures have actually been recorded. It can take several days for a check to clear, but you should treat it as money spent. The -@samp{cleared} report shows just that: +@code{cleared} report shows just that (note that the cleared report will +not format correctly for accounts that contain multiple commodities): @smallexample $ ledger -f drewr3.dat cleared @@ -473,7 +472,7 @@ $ ledger -f drewr3.dat cleared $ -243.60 0 @end smallexample -@noindent The first column shows the outstanding balance, the second column show the ``cleared'' balance. +@noindent The first column shows the outstanding balance, the second column shows the ``cleared'' balance. @node Using the Windows command line, , Cleared Report, Run Some Reports @subsection Using the Windows Command Line @cindex windows cmd.exe @@ -482,149 +481,6 @@ Using ledger under the windows command shell has one significant limitation. CMD.exe is limited to standard ASCII characters and as such cannot display any currency symbols other than dollar signs ($). -@node Command Line Quick Reference, , Run Some Reports, Ledger Tutorial -@section Command Line Quick Reference - -@menu -* Reporting Commands Quick Reference:: -* Basic Options Quick Reference:: -* Report Filtering Quick Reference:: -* Error Checking and Calculation Options:: -* Output Customization Quick Reference:: -* Grouping Options:: -* Commodity Reporting Quick Reference:: -@end menu - -@node Reporting Commands Quick Reference, Basic Options Quick Reference, Command Line Quick Reference, Command Line Quick Reference -@subsection Reporting Commands -@multitable @columnfractions .2 .8 -@item @strong{Report} @tab @strong{Description} -@item @code{balance} @tab Show account balances -@item @code{register} @tab Show all transactions with running total -@item @code{csv} @tab Show transactions in csv format, for exporting to other programs -@item @code{print} @tab Print transaction in a ledger readable format -@item @code{output} @tab Similar to print without included transactions -@item @code{xml} @tab Produce XML output of the register command -@item @code{emacs} @tab Produce Emacs lisp output -@item @code{equity} @tab Print account balances as transactions -@item @code{prices} @tab Print price history for matching commodities -@item @code{pricedb} @tab Print price history for matching commodities in ledger readable format -@item @code{xact} @tab Used to generate transactions based on previous postings -@end multitable - -@node Basic Options Quick Reference, Report Filtering Quick Reference, Reporting Commands Quick Reference, Command Line Quick Reference -@subsection Basic Options -@multitable @columnfractions .1 .25 .65 -@item @strong{Short} @tab @strong{Long} @tab @strong{Description} -@item @code{-h} @tab @code{--help} @tab prints summary of all options -@item @code{-v} @tab @code{--version} @tab prints version of ledger executable -@item @code{-f FILE} @tab @code{--file FILE} @tab read @file{FILE} as a ledger file -@item @code{-o FILE} @tab @code{--output FILE} @tab redirects output to @file{FILE} -@item @code{-i FILE} @tab @code{--init-file FILE} @tab specify options file -@item @code{-a NAME} @tab @code{--account NAME} @tab specify default account name for QIF file postings -@end multitable - -@node Report Filtering Quick Reference, Error Checking and Calculation Options, Basic Options Quick Reference, Command Line Quick Reference -@subsection Report Filtering -@multitable @columnfractions .1 .25 .65 -@item @strong{Short} @tab @strong{Long} @tab @strong{Description} -@item @code{-c} @tab @code{--current} @tab Display transaction on or before the current date -@item @code{-b DATE} @tab @code{--begin DATE} @tab Begin reports on or after @code{DATE} -@item @code{-e DATE} @tab @code{--end DATE} @tab Limits end date of transactions for report -@item @code{-p STR} @tab @code{--period} @tab Set report period to STR -@item @code{ } @tab @code{--period-sort} @tab Sort postings within each period -@item @code{-C} @tab @code{--cleared} @tab Display only cleared postings -@item @code{} @tab @code{--dc} @tab Display register or balance in debit/credit format -@item @code{-U} @tab @code{--uncleared} @tab Display only uncleared postings -@item @code{-R} @tab @code{--real} @tab Display only real postings -@item @code{-L} @tab @code{--actual} @tab Displays only actual postings, not automated -@item @code{-r} @tab @code{--related} @tab Display related postings -@item @code{} @tab @code{--budget} @tab Display how close your postings meet your budget -@item @code{} @tab @code{--add-budget} @tab Shows un-budgeted postings -@item @code{} @tab @code{--unbudgeted} @tab Shows only un-budgeted postings -@item @code{} @tab @code{--forecast} @tab Project balances into the future -@item @code{-l EXPR} @tab @code{--limit EXPR} @tab Limits postings in calculations -@item @code{-t EXPR} @tab @code{--amount} @tab Change value expression reported in register report -@item @code{-T EXPR} @tab @code{--total} @tab Change the value expression used for ``totals'' column in register and balance reports -@end multitable - -@node Error Checking and Calculation Options, Output Customization Quick Reference, Report Filtering Quick Reference, Command Line Quick Reference -@subsection Error Checking and Calculation Options - -@multitable @columnfractions .1 .25 .65 -@item @strong{Short} @tab @strong{Long} @tab @strong{Description} -@item @code{} @tab @code{--strict} @tab accounts, tags or commodities not previously declared will cause warnings -@item @code{} @tab @code{--pedantic} @tab accounts, tags or commodities not previously declared will cause errors -@item @code{} @tab @code{--check-payees} @tab enable strict and pedantic checking for payees as well as accounts, commodities and tags. -@item @code{} @tab @code{--immediate} @tab instructs ledger to evaluate calculations immediately rather than lazily -@end multitable - - -@node Output Customization Quick Reference, Grouping Options, Error Checking and Calculation Options, Command Line Quick Reference -@subsection Output Customization -@multitable @columnfractions .15 .4 .45 -@item @strong{Short} @tab @strong{Long} @tab @strong{Description} -@item @code{-n} @tab @code{--collapse} @tab Collapse transactions with multiple postings -@item @code{-s} @tab @code{--subtotal} @tab Report register as a single subtotal -@item @code{-P} @tab @code{--by-payee} @tab Report subtotals by payee -@item @code{-E} @tab @code{--empty} @tab Include empty accounts in report -@item @code{-W} @tab @code{--weekly} @tab Report posting totals by week -@item @code{-Y} @tab @code{--yearly} @tab Report posting totals by year -@item @code{} @tab @code{--dow} @tab report Posting totals by day of week -@item @code{-S EXPR} @tab @code{--sort EXPR} @tab Sorts a report using @code{EXPR} -@item @code{-w} @tab @code{--wide} @tab Assume 132 columns instead of 80 -@item @code{} @tab @code{--head N} @tab Report the first N postings -@item @code{} @tab @code{--tail N} @tab Report the last N postings -@item @code{} @tab @code{--pager prog} @tab Direct output @code{prog} pager program -@item @code{-A} @tab @code{--average} @tab Reports average posting value -@item @code{-D} @tab @code{--deviation} @tab Reports each posting deviation from the average -@item @code{-%} @tab @code{--percent} @tab Show subtotals in the balance report as percentages -@c @item @code{} @tab @code{--totals} @tab Include running total in the @code{xml} report -@item @code{} @tab @code{--pivot TAG} @tab produce a pivot table of the tag type specified -@item @code{-j} @tab @code{--amount-data} @tab Show only date and value column -@item @code{-J} @tab @code{--total-data} @tab Show only dates and totals -@item @code{-d EXPR} @tab @code{--display EXPR} @tab Limit only the display of certain postings -@item @code{-y STR} @tab @code{--date-format STR} @tab Change the basic date format used in reports -@item @code{-F STR} @tab @code{--format STR} @tab Set reporting format -@item @code{} @tab @code{--balance-format STR} @tab -@item @code{} @tab @code{--register-format STR} @tab -@item @code{} @tab @code{--print-format STR} @tab -@item @code{-j register} @tab @code{--plot-amount-format STR} @tab -@item @code{-J register} @tab @code{--plot-total-format STR} @tab -@item @code{} @tab @code{--equity-format STR} @tab -@item @code{} @tab @code{--prices-format STR} @tab -@item @code{-w register} @tab @code{--wide-register-format STR} @tab -@item @code{} @tab @code{--anon} @tab Print the ledger register with anonymized accounts and payees, useful for filing bug reports -@end multitable - -@node Grouping Options, Commodity Reporting Quick Reference, Output Customization Quick Reference, Command Line Quick Reference -@subsection Grouping Options -@multitable @columnfractions .1 .25 .65 -@item @strong{Short} @tab @strong{Long} @tab @strong{Description} -@item @code{-P} @tab @code{--by-payee} @tab Group postings by common payee names -@item @code{-D} @tab @code{--daily} @tab Group postings by day -@item @code{-W} @tab @code{--weekly} @tab Group postings by week -@item @code{-M} @tab @code{--Monthly} @tab Group postings by month -@item @code{} @tab @code{--quarterly} @tab Group postings by quarter -@item @code{-Y} @tab @code{--yearly} @tab Group postings by year -@item @code{} @tab @code{--dow} @tab Group by day of weeks -@item @code{-s} @tab @code{--subtotal} @tab Group posting together, similar to balance report -@end multitable - -@node Commodity Reporting Quick Reference, , Grouping Options, Command Line Quick Reference -@subsection Commodity Reporting - -@multitable @columnfractions .1 .25 .65 -@item @strong{Short} @tab @strong{Long} @tab @strong{Description} -@item @code{} @tab @code{--price-db FILE} @tab Use @file{FILE} for retrieving stored commodity prices -@item @code{-L MINS} @tab @code{--price-exp MINS} @tab Set expected freshness of prices in minutes -@item @code{-Q} @tab @code{--download} @tab Download quotes using @code{getquote} -@item @code{} @tab @code{--getquote} @tab Sets path to a user defined script to download commodity prices. -@item @code{-O} @tab @code{--quantity} @tab Report commodity totals without conversion -@item @code{-B} @tab @code{--basis} @tab Report cost basis -@item @code{-V} @tab @code{--market} @tab Report last known market value -@item @code{-G} @tab @code{--gain} @tab Report net gain loss for commodities that have a price history -@end multitable @node Principles of Accounting, Keeping a Journal, Ledger Tutorial , Top @chapter Principles of Accounting with Ledger @@ -770,7 +626,7 @@ ledger -M register expenses:auto @end example This assumes, of course, that you use account names like -@samp{Expenses:Auto:Gas} and @samp{Expenses:Auto:Repair}. +@code{Expenses:Auto:Gas} and @code{Expenses:Auto:Repair}. @menu * Tracking reimbursable expenses:: @@ -872,8 +728,8 @@ It's easier shown than said: And now the reimbursements account is paid off, accounts payable is paid off, and $100.00 has been effectively transferred from the company's checking account to your personal checking account. The -money simply ``waited''---in both @samp{Assets:Reimbursements:Company -XYZ}, and @samp{Company XYZ:Accounts Payable:Your Name}---until such +money simply ``waited''---in both @code{Assets:Reimbursements:Company +XYZ}, and @code{Company XYZ:Accounts Payable:Your Name}---until such time as it could be paid off. The value of tracking expenses from both sides like that is that you @@ -917,7 +773,7 @@ apply account Company XYZ end apply account @end smallexample -(Note: The @samp{apply account} above means that all accounts mentioned in +(Note: The @code{apply account} above means that all accounts mentioned in the file are children of that account. In this case it means that all activity in the file relates to Company XYZ). @@ -983,8 +839,8 @@ P 2004/06/21 02:18:02 AAPL $32.91 P 2004/06/21 02:18:02 AU $400.00 @end smallexample -Specify the price history to use with the @option{--price-db} option, -with the @option{-V} option to report in terms of current market +Specify the price history to use with the @code{--price-db} option, +with the @code{-V} option to report in terms of current market value: @example @@ -1054,7 +910,7 @@ or days, it should be possible to convert between the various forms. Doing this requires the use of commodity equivalencies. For example, you might have the following two postings, one which -transfers an hour of time into a @samp{Billable} account, and another +transfers an hour of time into a @code{Billable} account, and another which decreases the same account by ten minutes. The resulting report will indicate that fifty minutes remain: @@ -1088,13 +944,13 @@ C 1.00 Gb = 1024 Mb C 1.00 Tb = 1024 Gb @end smallexample -Each of these definitions correlates a commodity (such as @samp{Kb}) +Each of these definitions correlates a commodity (such as @code{Kb}) and a default precision, with a certain quantity of another commodity. In the above example, kilobytes are reported with two decimal places of precision and each kilobyte is equal to 1024 bytes. Equivalency chains can be as long as desired. Whenever a commodity -would report as a decimal amount (less than @samp{1.00}), the next +would report as a decimal amount (less than @code{1.00}), the next smallest commodity is used. If a commodity could be reported in terms of a higher commodity without resulting to a partial fraction, then the larger commodity is used. @@ -1119,8 +975,8 @@ EverQuest account: Now your EverQuest:Inventory has 3 apples and 5 steaks in it. The amounts are negative, because you are taking @emph{from} Black's Tavern in order to add to your Inventory account. Note that you don't -have to use @samp{Places:Black's Tavern} as the source account. You -could use @samp{EverQuest:System} to represent the fact that you +have to use @code{Places:Black's Tavern} as the source account. You +could use @code{EverQuest:System} to represent the fact that you acquired them online. The only purpose for choosing one kind of source account over another is for generate more informative reports later on. The more you know, the better analysis you can perform. @@ -1173,7 +1029,7 @@ assets is greater than the absolute value of your starting equity, it means you are making money. Clear as mud? Keep thinking about it. Until you figure it out, put -@samp{-Equity} at the end of your balance command, to remove the +@code{-Equity} at the end of your balance command, to remove the confusing figure from the total. @node Dealing with Petty Cash, Working with multiple funds and accounts, Understanding Equity, Principles of Accounting @@ -1186,7 +1042,7 @@ a few large ones, as with checks. One solution is: don't bother. Move your spending to a debit card, but in general ignore cash. Once you withdraw it from the ATM, mark -it as already spent to an @samp{Expenses:Cash} category: +it as already spent to an @code{Expenses:Cash} category: @smallexample 2004/03/15 ATM @@ -1195,7 +1051,7 @@ it as already spent to an @samp{Expenses:Cash} category: @end smallexample If at some point you make a large cash expense that you want to track, -just ``move'' the amount of the expense from @samp{Expenses:Cash} into +just ``move'' the amount of the expense from @code{Expenses:Cash} into the target account: @smallexample @@ -1235,8 +1091,8 @@ reserves resources for later: The problem with this kind of setup is that when you spend money, it comes from two or more places at once: the account and the fund. And yet, the correlation of amounts between funds and accounts is rarely -one-to-one. What if the school fund has @samp{$500.00}, but -@samp{$400.00} of that comes from Checking, and @samp{$100.00} from +one-to-one. What if the school fund has @code{$500.00}, but +@code{$400.00} of that comes from Checking, and @code{$100.00} from Savings? Traditional finance packages require that the money reside in only one @@ -1274,14 +1130,14 @@ account: 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 +@code{Assets} account, because otherwise the balance won't make much sense: @example ledger bal -^Assets @end example -If the @option{--real} option is used, the report will be in terms of +If the @code{--real} option is used, the report will be in terms of the real accounts: @example @@ -1303,7 +1159,7 @@ The second way of tracking funds is to use transaction codes. In this respect the codes become like virtual accounts that embrace the entire set of postings. Basically, we are associating a transaction with a fund by setting its code. Here are two transactions that deposit money -into, and spend money from, the @samp{Funds:School} fund: +into, and spend money from, the @code{Funds:School} fund: @smallexample 2004/03/25 (Funds:School) Donations @@ -1320,10 +1176,10 @@ balance or registers reports will reflect this. That the transactions relate to a particular fund is kept only in the code. How does this become a fund report? By using the -@option{--code-as-payee} option, you can generate a register report +@code{--code-as-payee} option, you can generate a register report where the payee for each posting shows the code. Alone, this is not terribly interesting; but when combined with the -@option{--by-payee} option, you will now see account subtotals for any +@code{--by-payee} option, you will now see account subtotals for any postings related to a specific fund. So, to see the current monetary balances of all funds, the command would be: @@ -1331,7 +1187,7 @@ monetary balances of all funds, the command would be: ledger --code-as-payee -P reg ^Assets @end smallexample -Or to see a particular funds expenses, the @samp{School} fund in this +Or to see a particular funds expenses, the @code{School} fund in this case: @smallexample @@ -1369,7 +1225,7 @@ For example, you do not need to tell Ledger about the accounts you use. Any time Ledger sees a posting involving an account it knows nothing about, it will create it@footnote{This also means if you misspell an account it will end up getting counted separately from what -you intended. The provided Emacs major mode provides for automatically +you intended. The provided EMACS major mode provides for automatically filling in account names.}. If you use a commodity that is new to Ledger, it will create that commodity, and determine its display characteristics (placement of the symbol before or after the amount, @@ -1384,8 +1240,9 @@ posting. * Currency and Commodities:: * Keeping it Consistent:: * Journal Format:: +* Converting from other formats:: * Archiving Previous Years :: -* Using Emacs:: +* Using EMACS:: @end menu @node Most Basic Entry, Starting up, Keeping a Journal, Keeping a Journal @@ -1413,7 +1270,7 @@ balanced amount, if it is the same as the first line: @end smallexample For this transaction, Ledger will figure out that $-23.00 must come from -@samp{Assets:Checking} in order to balance the transaction. +@code{Assets:Checking} in order to balance the transaction. Also note the structure of the account entries. There is an implied hierarchy established by separating with colons (see @pxref{Structuring @@ -1455,9 +1312,9 @@ your opening balance entry could look like this: Assets:Joint Checking $800.14 Assets:Other Checking $63.44 Assets:Savings $2805.54 - Assets:Investments:401K:Deferred 100.0000 VIFSX @ $80.5227 - Assets:Investments:401K:Matching 50.0000 VIFSX @ $83.7015 - Assets:Investments:IRA 250.0000 VTHRX @ $20.5324 + Assets:Investments:401K:Deferred 100.0000 VIFSX @@ $80.5227 + Assets:Investments:401K:Matching 50.0000 VIFSX @@ $83.7015 + Assets:Investments:IRA 250.0000 VTHRX @@ $20.5324 Liabilities:Mortgage $-175634.88 Liabilities:Car Loan $-3494.26 Liabilities:Visa -$1762.44 @@ -1721,6 +1578,145 @@ its amount is null. @node Complete control over commodity pricing, , Fixing Lot Prices, Currency and Commodities @subsection Complete control over commodity pricing +Ledger allows you to have very detailed control over how your +commodities are valued. You can fine tune the results given using the +@code{--market} or @code{--exchange} options. There are now several +points of interception, you can specify the valuation method: +@enumerate + @item on a commodity itself + @item on a posting, via metadata (affect is largely the same as #1) + @item on an xact, which then applies to all postings in that xact + @item on any posting via an automated transaction + @item on a per-account basis + @item on a per-commodity basis + @item by changing the journal default of @code{market} +@end enumerate + +Fixated pricing (such as @code{@{=$20@})} still plays a role in this scheme. As far as +valuation goes, it's shorthand for writing @code{((s,d,t -> market($20,d,t)))}. + + +A valuation function receives three arguments: + +@table @code +@item source + A string identifying the commodity whose price is being asked for + (example: "EUR") +@item date + The reference date the price should be relative. +@item target +A string identifying the ``target'' commodity, or the commodity the + returned price should be in. This argument is null if @code{--market} + was used instead of @code{--exchange}. +@end table + +The valuation function should return an amount. If you've written your +function in Python, you can return something like @code{Amount("$100")}. If the +function returns an explicit value, that value is always used, regardless +of the commodity, the date, or the desired target commodity. For example, + +@smallexample +define myfunc_seven(s, d, t) = 7 EUR +@end smallexample + +In order to specify a fixed price, but still valuate that price into the +target commodity, use something like this: +@smallexample +define myfunc_five(s, d, t) = market(5 EUR, d, t) +@end smallexample + +The @code{value} directive sets the valuation used for all commodities +used in the rest of the data stream. This is the fallback, if nothing +more specific is found. +@smallexample +value myfunc_seven +@end smallexample + +You can set a specific valuation function on a per-commodity basis. +Instead of defining a function, you can also pass a lambda. +@smallexample +commodity $ + value s, d, t -> 6 EUR +@end smallexample + +Each account can also provide a default valuation function for any +commodities transferred to that account. + +@smallexample +account Expenses:Food5 + value myfunc_five +@end smallexample + +The metadata field @code{Value}, if found, overrides the valuation function +on a transaction-wide or per-posting basis. + +@smallexample += @@XACT and Food + ; Value:: 8 EUR + (Equity) $1 + += @@POST and Dining + (Expenses:Food9) $1 + ; Value:: 9 EUR +@end smallexample + +Lastly, you can specify the valuation function/value for any specific +amount using the @code{(( ))} commodity annotation. + +@smallexample +2012-03-02 KFC + Expenses:Food2 $1 ((2 EUR)) + Assets:Cash2 + +2012-03-03 KFC + Expenses:Food3 $1 + ; Value:: 3 EUR + Assets:Cash3 + +2012-03-04 KFC + ; Value:: 4 EUR + Expenses:Food4 $1 + Assets:Cash4 + +2012-03-05 KFC + Expenses:Food5 $1 + Assets:Cash5 + +2012-03-06 KFC + Expenses:Food6 $1 + Assets:Cash6 + +2012-03-07 KFC + Expenses:Food7 1 CAD + Assets:Cas7 + +2012-03-08 XACT + Expenses:Food8 $1 + Assets:Cash8 + +2012-03-09 POST + Expenses:Dining9 $1 + Assets:Cash9 +@end smallexample + + +@smallexample +ledger reg -V food +12-Mar-02 KFC Expenses:Food2 2 EUR 2 EUR +12-Mar-03 KFC <Adjustment> -1 EUR 1 EUR + Expenses:Food3 3 EUR 4 EUR +12-Mar-04 KFC <Adjustment> -2 EUR 2 EUR + Expenses:Food4 4 EUR 6 EUR +12-Mar-05 KFC <Adjustment> -3 EUR 3 EUR + Expenses:Food5 5 EUR 8 EUR +12-Mar-06 KFC <Adjustment> -4 EUR 4 EUR + Expenses:Food6 6 EUR 10 EUR +12-Mar-07 KFC Expenses:Food7 7 EUR 17 EUR +12-Mar-08 XACT Expenses:Food8 8 EUR 25 EUR +12-Mar-09 POST (Expenses:Food9) 9 EUR 34 EUR +@end smallexample + + @node Keeping it Consistent, Journal Format, Currency and Commodities, Keeping a Journal @section Keeping it Consistent @@ -1737,7 +1733,7 @@ account Expenses account Expenses:Utilities ... @end smallexample -Using the @samp{--strict} option will cause Ledger to complain if any accounts are not previously defined: +Using the @code{--strict} option will cause Ledger to complain if any accounts are not previously defined: @smallexample 15:27:39 ~/ledger (next) > ledger bal --strict Warning: "FinanceData/Master.dat", line 6: Unknown account 'Liabilities:Tithe Owed' @@ -1745,14 +1741,14 @@ Warning: "FinanceData/Master.dat", line 8: Unknown account 'Liabilities:Tithe Ow Warning: "FinanceData/Master.dat", line 15: Unknown account 'Allocation:Equities:Domestic' @end smallexample -If you have a large Ledger register already created use the @samp{accounts} command to get started: +If you have a large Ledger register already created use the @code{accounts} command to get started: @smallexample ledger accounts >> Accounts.dat @end smallexample -@noindent You will have to edit this file to add the @samp{account} directive. +@noindent You will have to edit this file to add the @code{account} directive. -@node Journal Format, Archiving Previous Years , Keeping it Consistent, Keeping a Journal +@node Journal Format, Converting from other formats, Keeping it Consistent, Keeping a Journal @section Journal Format The ledger file format is quite simple, but also very flexible. It @@ -1779,11 +1775,11 @@ transaction's account postings. The format of the first line is: DATE[=EDATE] [*|!] [(CODE)] DESC @end smallexample -If @samp{*} appears after the date (with optional effective date), it +If @code{*} appears after the date (with optional effective date), it indicates the transaction is ``cleared'', which can mean whatever the user -wants it to mean. If @samp{!} appears after the date, it indicates d +wants it to mean. If @code{!} appears after the date, it indicates d the transaction is ``pending''; i.e., tentatively cleared from the user's -point of view, but not yet actually cleared. If a @samp{CODE} appears +point of view, but not yet actually cleared. If a @code{CODE} appears in parentheses, it may be used to indicate a check number, or the type of the posting. Following these is the payee, or a description of the posting. @@ -1794,18 +1790,18 @@ The format of each following posting is: ACCOUNT AMOUNT [; NOTE] @end smallexample -The @samp{ACCOUNT} may be surrounded by parentheses if it is a virtual +The @code{ACCOUNT} may be surrounded by parentheses if it is a virtual posting, or square brackets if it is a virtual posting that -must balance. The @samp{AMOUNT} can be followed by a per-unit -posting cost, by specifying @samp{@@ AMOUNT}, or a complete -posting cost with @samp{@@@@ AMOUNT}. Lastly, the @samp{NOTE} may +must balance. The @code{AMOUNT} can be followed by a per-unit +posting cost, by specifying @code{@@ AMOUNT}, or a complete +posting cost with @code{@@@@ AMOUNT}. Lastly, the @code{NOTE} may specify an actual and/or effective date for the posting by using -the syntax @samp{[ACTUAL_DATE]} or @samp{[=EFFECTIVE_DATE]} or -@samp{[ACTUAL_DATE=EFFECTIVE_DATE]}.(See @pxref{Virtual postings}) +the syntax @code{[ACTUAL_DATE]} or @code{[=EFFECTIVE_DATE]} or +@code{[ACTUAL_DATE=EFFECTIVE_DATE]}.(See @pxref{Virtual postings}) @item P Specifies a historical price for a commodity. These are usually found -in a pricing history file (see the @option{-Q} option). The syntax +in a pricing history file (see the @code{-Q} option). The syntax is: @smallexample P DATE SYMBOL PRICE @@ -1818,7 +1814,7 @@ sign. After this initial line there should be a set of one or more postings, just as if it were normal transaction. If the amounts of the postings have no commodity, they will be applied as modifiers to -whichever real posting is matched by the value expression(See @pxref{Automated transactions}). +whichever real posting is matched by the value expression(See @pxref{Automated Transactions}). @item ~ A period transaction. A period expression must appear after the tilde. @@ -1848,8 +1844,8 @@ Command directives must occur at the beginning of a line. Use of ! and @item account Pre-declare valid account names. This only has effect if -@samp{--strict} or @samp{--pedantic} is used (see below). The -@samp{account} directive supports several optional sub-directives, if +@code{--strict} or @code{--pedantic} is used (see below). The +@code{account} directive supports several optional sub-directives, if they immediately follow the account directive and if they begin with whitespace: @@ -1864,14 +1860,14 @@ whitespace: default @end smallexample -The @samp{note} sub-directive associates a textual note with the account. This can -be accessed later using the @samp{note} valexpr function in any account context. +The @code{note} sub-directive associates a textual note with the account. This can +be accessed later using the @code{note} valexpr function in any account context. -The @samp{alias} sub-directive, which can occur multiple times, allows the alias to +The @code{alias} sub-directive, which can occur multiple times, allows the alias to be used in place of the full account name anywhere that account names are allowed. -The @samp{payee} sub-directive, which can occur multiple times, provides regexps +The @code{payee} sub-directive, which can occur multiple times, provides regexps that identify the account if that payee is encountered and an account within its transaction ends in the name "Unknown". Example: @@ -1881,13 +1877,13 @@ its transaction ends in the name "Unknown". Example: Assets:Cash @end smallexample -The @samp{check} and @samp{assert} directives warn or error (respectively) if the given +The @code{check} and @code{assert} directives warn or error (respectively) if the given value expression evaluates to false within the context of any posting. -The @samp{eval} directive evaluates the value expression in the context of the +The @code{eval} directive evaluates the value expression in the context of the account at the time of definition. At the moment this has little value. -The @samp{default} directive specifies that this account should be used as the +The @code{default} directive specifies that this account should be used as the ``balancing account'' for any future transactions that contain only a single posting. @@ -1996,13 +1992,13 @@ check <VALUE EXPRESSION BOOLEAN RESULT> Start a block comment, closed by @code{end comment}. @item commodity -Pre-declare commodity names. This only has effect if @samp{--strict} or -@samp{--pedantic} is used (see below). +Pre-declare commodity names. This only has effect if @code{--strict} or +@code{--pedantic} is used (see below). commodity $ commodity CAD -The @samp{commodity} directive supports several optional sub-directives, if they +The @code{commodity} directive supports several optional sub-directives, if they immediately follow the commodity directive and if they begin with whitespace: @smallexample @@ -2013,18 +2009,18 @@ immediately follow the commodity directive and if they begin with whitespace: default @end smallexample -The @samp{note} sub-directive associates a textual note with the commodity. At +The @code{note} sub-directive associates a textual note with the commodity. At present this has no value other than documentation. -The @samp{format} directive gives you a way to tell Ledger how to format this +The @code{format} directive gives you a way to tell Ledger how to format this commodity. In future using this directive will disable Ledger's observation of other ways that commodity is used, and will provide the ``canonical'' representation. -The @samp{nomarket} directive states that the commodity's price should never be +The @code{nomarket} directive states that the commodity's price should never be auto-downloaded. -The @samp{default} directive marks this as the ``default'' commodity. +The @code{default} directive marks this as the ``default'' commodity. @item define @c instance_t::define_directive in textual.cc @@ -2047,13 +2043,48 @@ Closes block commands like @code{tag} or @code{comment}. @item fixed @c instance_t::fixed_directive in textual.cc +A fixed block is used to set fixated prices (@pxref{Fixated prices}) for a series of +transactions. It's purely a typing saver, for use when entering many +transactions with fixated prices. + +Thus, the following: +@smallexample +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 +@end smallexample +is equivalent to this: +@smallexample + 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 @strong{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 @c instance_t::include_directive in textual.cc Include the stated file as if it were part of the current file. @item payee @c instance_t::payee_mapping_directive in textual.cc -The @samp{payee} directive supports one optional sub-directive, if it immediately +The @code{payee} directive supports one optional sub-directive, if it immediately follows the payee directive and if it begins with whitespace: @smallexample @@ -2061,7 +2092,7 @@ follows the payee directive and if it begins with whitespace: alias KENTUCKY FRIED CHICKEN @end smallexample -The @samp{alias} directive provides a regexp which, if it matches a parsed payee, +The @code{alias} directive provides a regexp which, if it matches a parsed payee, the declared payee name is substituted: @smallexample @@ -2121,8 +2152,8 @@ Note that anything following ``@code{end tag}'' is ignored. placing the name of the tag that is being closed is a simple way to keep track. @item tag -Pre-declares tag names. This only has effect if @samp{--strict} or -@samp{--pedantic} is used (see below). +Pre-declares tag names. This only has effect if @code{--strict} or +@code{--pedantic} is used (see below). @smallexample tag Receipt @@ -2138,7 +2169,7 @@ follow the tag directive and if they begin with whitespace: assert value != "foobar" @end smallexample -The @samp{check} and @samp{assert} directives warn or error (respectively) if the given +The @code{check} and @code{assert} directives warn or error (respectively) if the given value expression evaluates to false within the context of any use of the related tag. In such a context, ``value'' is bound to the value of the tag (which may not be a string if typed-metadata is used!). Such checks or @@ -2146,13 +2177,13 @@ assertions are not called if no value is given. @item test @c instance_t::comment_directive in textual.cc -This is a synonym for @code{comment} and must be closed by and @code{end} tag. +This is a synonym for @code{comment} and must be closed by an @code{end} tag. @item year @c instance_t::year_directive in textual.cc Denotes the year used for all subsequent transactions that give a date -without a year. The year should appear immediately after the Y, for -example: @samp{year 2004}. This is useful at the beginning of a file, to +without a year. The year should appear immediately after the directive, for +example: @code{year 2004}. This is useful at the beginning of a file, to specify the year for that file. If all transactions specify a year, however, this command has no effect. @@ -2205,7 +2236,21 @@ timelog files. See the timeclock's documentation for more info on the syntax of its timelog files. @end table -@node Archiving Previous Years , Using Emacs, Journal Format, Keeping a Journal +@node Converting from other formats, Archiving Previous Years , Journal Format, Keeping a Journal +@section Converting from other formats +There are numerous tools to help convert various formats to a Ledger +file. Most banks will generate a commas separated value file that can +easily be parsed into Ledger format using one of those tools. Some of the more popular tools are: +@itemize +@item @code{icsv2ledger} +@item @code{csvToLedger} +@item @code{CSV2Ledger} +@end itemize +@noindent Directly pulling information from banks is outside the scope of Ledger's +function. + + +@node Archiving Previous Years , Using EMACS, Converting from other formats, Keeping a Journal @section Archiving Previous Years @@ -2254,7 +2299,7 @@ they were before the data was split. How often should you split your ledger? You never need to, if you don't want to. Even eighty years of data will not slow down ledger -much---and that's just using present day hardware! Or, you can keep +much, and that's just using present day hardware! Or, you can keep the previous and current year in one file, and each year before that in its own file. It's really up to you, and how you want to organize your finances. For those who also keep an accurate paper trail, it @@ -2264,45 +2309,52 @@ any electronic statements received during the year. In the arena of organization, just keep in mind this maxim: Do whatever keeps you doing it. -@node Using Emacs, , Archiving Previous Years , Keeping a Journal -@section Using Emacs to Maintain Your Journal -@cindex Emacs +@node Using EMACS, , Archiving Previous Years , Keeping a Journal +@section Using EMACS to Maintain Your Journal +@cindex EMACS @menu -* running ledger-mode:: +* Running ledger-mode:: * Working with entries:: * Reconciling accounts:: * Generating Reports:: @end menu -@node running ledger-mode, Working with entries, Using Emacs, Using Emacs -@subsection Running ledger-mode +@node Running ledger-mode, Working with entries, Using EMACS, Using EMACS +@subsection Running ledger-mode in EMACS Journal files are simple free text files easily modified by any text -editor. A special mode for Emacs is included with the source +editor. A special mode for EMACS is included with the source distribution. -@cindex Emacs .emacs file -To use the Emacs mode, copy the several lisp files from the source lisp -directory your your @file{site-lisp} directory and add the following line -to your @file{.emacs} (or equivalent, @file{~/Aquamacs/Preferences.el} -for Aquamacs on Mac OS X) +@cindex EMACS .emacs file + +Add the following line to your @file{.emacs} (or equivalent, +@file{~/Aquamacs/Preferences.el} for Aquamacs on Mac OS X) +@smallexample +(load "ldg-new") +@end smallexample + +Copy the several lisp files from the source lisp directory your your +@file{site-lisp} directory, or add the ledger lisp source directory to +your EMACS load path by adding: @smallexample -(load "ledger") +(add-to-list 'load-path "~/ledger/lisp") @end smallexample +@noindent to your @file{.emacs} file. To trigger ledger mode when you visit a journal file, the first line of each of your journal files should be: @smallexample ; -*-ledger-*- @end smallexample -To enter ledger-mode on a new file, type M-x ledger-mode. +To enter ledger-mode on a new file, type @command{M-x ledger-mode}. -Once you have loaded a Journal file into Emacs, you have several +Once you have loaded a Journal file into EMACS, you have several commands available to make entering, clearing and reconciling transactions and producing reports: -@cindex Emacs commands +@cindex EMACS commands @table @code @item C-i or <TAB> auto complete entry @@ -2342,7 +2394,7 @@ kill the ledger report buffer * Generating Reports:: @end menu -@node Working with entries, Reconciling accounts, running ledger-mode, Using Emacs +@node Working with entries, Reconciling accounts, Running ledger-mode, Using EMACS @subsection Working with entries @menu * Manual Entry Support:: @@ -2360,13 +2412,13 @@ kill the ledger report buffer In most financial programs, some sort of auto-completion is available to save typing and improve accuracy. Ledger doesn't leave you hanging, @code{ledger-mode} provides tab completion on all portions of an entry. -Type a portion of the payee and hit <TAB>, and @code{ledger-mode} will +Type a portion of the payee and hit @code{<TAB>}, and @code{ledger-mode} will suggest a completion. When filling in the account type the first few -letters followed by a <TAB> and the account will be filled in. For -example typing @samp{Ex<TAB>Au<TAB>F<TAB>} would yield -@samp{Expenses:Auto:Fuel} if you had previously used that account in +letters followed by a @code{<TAB>} and the account will be filled in. For +example typing @code{Ex<TAB>Au<TAB>F<TAB>} would yield +@code{Expenses:Auto:Fuel} if you had previously used that account in this journal. If there are more than one account with similar starting, -hitting <TAB> multiple times will iterate through them. This is a good +hitting @code{<TAB>} multiple times will iterate through them. This is a good habit to get in to prevent misspellings of accounts. Remember Ledger does not validate the names of payees or account so a misspelled account will be interpreted as a new account by ledger. @@ -2374,10 +2426,10 @@ will be interpreted as a new account by ledger. @node Automagically Adding new entries, Clearing Transactions, Manual Entry Support, Working with entries @subsubsection Automagically Adding new entries -@cindex new transactions in Emacs -@cindex Emacs, adding new transactions +@cindex new transactions in EMACS +@cindex EMACS, adding new transactions @code{C-c C-a} will run the @code{ledger entry} command (@pxref{entry -and xact}) from within Emacs. When typed, the mini-buffer will appear +and xact}) from within EMACS. When typed, the mini-buffer will appear with the current year and month, waiting for you to enter the day and the payee. Ledger will generate a new entry based on the most recent entry for that payee, using the amount and accounts from that @@ -2389,12 +2441,12 @@ payee. For example, if your journal contains an entry Expenses:Tips $2.55 Liabilities:MasterCard $-15.00 @end smallexample -@noindent and you type @samp{C-c C-a}, the mini-buffer will appear showing the +@noindent and you type @code{C-c C-a}, the mini-buffer will appear showing the current year and month. If you complete the mini-buffer entry by typing @smallexample Entry: 2011/11/28 viva food 34 tip 7 <enter> @end smallexample -@noindent Emacs will add the following entry to your journal: +@noindent EMACS will add the following entry to your journal: @smallexample 2011/11/30 Viva Italiano Expenses:Food $34.00 @@ -2405,8 +2457,8 @@ Entry: 2011/11/28 viva food 34 tip 7 <enter> ordered by date, not necessarily at the bottom of the file. @node Clearing Transactions, , Automagically Adding new entries, Working with entries @subsubsection Clearing Transactions and Postings -@cindex clearing transactions in Emacs -@cindex Emacs, clear transaction +@cindex clearing transactions in EMACS +@cindex EMACS, clear transaction @code{C-c C-e} will place an asterisk after the date in the current transaction. The tells ledger the transaction has been cleared through your bank (or whatever else you want the concept to mean) @@ -2425,16 +2477,16 @@ your bank (or whatever else you want the concept to mean) @end smallexample If, for some reason you need to clear a specific posting in the -transaction you can type @samp{C-c C-c} and the posting at point will be +transaction you can type @code{C-c C-c} and the posting at point will be toggled. -@node Reconciling accounts, Generating Reports, Working with entries, Using Emacs +@node Reconciling accounts, Generating Reports, Working with entries, Using EMACS @subsection Reconciling accounts In the reconcile buffer, use SPACE to toggle the cleared status of a transaction, C-x C-s to save changes (to the ledger file as well). -@node Generating Reports, , Reconciling accounts, Using Emacs +@node Generating Reports, , Reconciling accounts, Using EMACS @subsection Generating Reports The ledger reports command asks the user to select a report to run then @@ -2486,10 +2538,11 @@ kill the report buffer * Virtual posting costs:: * Commodity prices:: * Prices vs. costs:: +* Fixated prices:: * Lot dates:: * Lot notes:: * Lot value expressions:: -* Automated transactions:: +* Automated Transactions:: @end menu @node Basic format, Eliding amounts, Transactions , Transactions @@ -2771,7 +2824,7 @@ there are some tricks up Ledger's sleeve... You can use virtual accounts to transfer amounts to an account on the sly, bypassing the balancing requirement. The trick is that these postings are not -considered ``real'', and can be removed from all reports using @samp{--real}. +considered ``real'', and can be removed from all reports using @code{--real}. To specify a virtual account, surround the account name with parentheses: @@ -2917,12 +2970,12 @@ resulting posting cost is $50.00 per share. @node Explicit posting costs, Posting cost expressions, Posting cost, Transactions @section Explicit posting costs -You can make any posting's cost explicit using the @ symbol after the amount +You can make any posting's cost explicit using the @@ symbol after the amount or amount expression: @smallexample 2012-03-10 My Broker - Assets:Brokerage 10 AAPL @ $50.00 + Assets:Brokerage 10 AAPL @@ $50.00 Assets:Brokerage:Cash $-500.00 @end smallexample @@ -2931,7 +2984,7 @@ the first posting's cost, you can elide the other amount: @smallexample 2012-03-10 My Broker - Assets:Brokerage 10 AAPL @ $50.00 + Assets:Brokerage 10 AAPL @@ $50.00 Assets:Brokerage:Cash @end smallexample @@ -2948,7 +3001,7 @@ source account. Whenever a commodity is exchanged like this, the commodity moved to the target account is considered "secondary", while the commodity used for purchasing and tracked in the cost is "primary". -Said another way, whenever Ledger sees a posting cost of the form "AMOUNT @ +Said another way, whenever Ledger sees a posting cost of the form "AMOUNT @@ AMOUNT", the commodity used in the second amount is marked "primary". The only meaning a primary commodity has is that -V flag will never convert a @@ -2961,7 +3014,7 @@ Just as you can have amount expressions, you can have posting expressions: @smallexample 2012-03-10 My Broker - Assets:Brokerage 10 AAPL @ ($500.00 / 10) + Assets:Brokerage 10 AAPL @@ ($500.00 / 10) Assets:Brokerage:Cash @end smallexample @@ -2969,20 +3022,20 @@ You can even have both: @smallexample 2012-03-10 My Broker - Assets:Brokerage (5 AAPL * 2) @ ($500.00 / 10) + Assets:Brokerage (5 AAPL * 2) @@ ($500.00 / 10) Assets:Brokerage:Cash @end smallexample @node Total posting costs, Virtual posting costs, Posting cost expressions, Transactions @section Total posting costs -The cost figure following the @ character specifies the @emph{per-unit} price for +The cost figure following the @@ character specifies the @emph{per-unit} price for the commodity being transferred. If you'd like to specify the total cost instead, use @@@@: @smallexample 2012-03-10 My Broker - Assets:Brokerage 10 AAPL @@@@ $500.00 + Assets:Brokerage 10 AAPL @@ $500.00 Assets:Brokerage:Cash @end smallexample @@ -3014,7 +3067,7 @@ of an exceptional transaction, surround the @@ or @@@@ with parentheses: When a transaction occurs that exchange one commodity for another, Ledger records that commodity price not only within its internal price database, but also attached to the commodity itself. Usually this fact remains invisible to -the user, unless you turn on @samp{--lot-prices} to show these hidden price figures. +the user, unless you turn on @code{--lot-prices} to show these hidden price figures. For example, consider the stock sale given above: @@ -3096,7 +3149,7 @@ Plus, it comes with dangers. This works fine: @smallexample 2012-04-10 My Broker - Assets:Brokerage 10 AAPL @ $50.00 + Assets:Brokerage 10 AAPL @@ $50.00 Assets:Brokerage:Cash $750.00 2012-04-10 My Broker @@ -3114,7 +3167,7 @@ Plus, it comes with dangers. This works fine: @smallexample 2012-04-10 My Broker - Assets:Brokerage 10 AAPL @ $50.00 + Assets:Brokerage 10 AAPL @@ $50.00 Assets:Brokerage:Cash $750.00 2012-04-10 My Broker @@ -3131,7 +3184,7 @@ Plus, it comes with dangers. This works fine: And in cases where the amounts do not divide into whole figure and must be rounded, the capital gains figure could be off by a cent. Use with caution. -@node Prices vs. costs, Lot dates, Commodity prices, Transactions +@node Prices vs. costs, Fixated prices, Commodity prices, Transactions @section Prices vs. costs Because lot pricing provides enough information to infer the cost, the @@ -3139,7 +3192,7 @@ following two transactions are equivalent: @smallexample 2012-04-10 My Broker - Assets:Brokerage 10 AAPL @ $50.00 + Assets:Brokerage 10 AAPL @@ $50.00 Assets:Brokerage:Cash $750.00 2012-04-10 My Broker @@ -3151,7 +3204,8 @@ However, note that what you see in some reports may differ, for example in the print report. Functionally, however, there is no difference, and neither the register nor the balance report are sensitive to this difference. -@section Fixated prices +@node Fixated prices, Lot dates, Prices vs. costs, Transactions +@section Fixated prices and costs If you buy a stock last year, and ask for its value today, Ledger will consult its price database to see what the most recent price for that stock is. You @@ -3170,14 +3224,7 @@ else happens to the stock in the meantime. Fixated prices are a special case of using lot valuation expressions (see below) to fix the value of a commodity lot. -@menu -* Fixated costs:: -@end menu - -@node Fixated costs, , Prices vs. costs, Prices vs. costs -@subsection Fixated costs - -Since price annotations are costs are largely interchangeable and a matter of +Since price annotations and costs are largely interchangeable and a matter of preference, there is an equivalent syntax for specified fixated prices by way of the cost: @@ -3188,13 +3235,13 @@ of the cost: @end smallexample This is the same as the previous transaction, with the same caveats found in -the section ``Prices vs. costs''. +@ref{Prices vs. costs}. -@node Lot dates, Lot notes, Prices vs. costs, Transactions +@node Lot dates, Lot notes, Fixated prices, Transactions @section Lot dates In addition to lot prices, you can specify lot dates and reveal them with -@samp{--lot-dates}. Other than that, however, they have no special meaning to +@code{--lot-dates}. Other than that, however, they have no special meaning to Ledger. They are specified after the amount in square brackets (the same way that dates are parsed in value expressions): @@ -3210,7 +3257,7 @@ that dates are parsed in value expressions): You can also associate arbitrary notes for your own record keeping in parentheses, and reveal them with --lot-notes. One caveat is that the note -cannot begin with an @ character, as that would indicate a virtual cost: +cannot begin with an @@ character, as that would indicate a virtual cost: @smallexample 2012-04-10 My Broker @@ -3222,9 +3269,9 @@ cannot begin with an @ character, as that would indicate a virtual cost: You can any combination of lot prices, dates or notes, in any order. They are all optional. -To show all lot information in a report, use @samp{--lots}. +To show all lot information in a report, use @code{--lots}. -@node Lot value expressions, Automated transactions, Lot notes, Transactions +@node Lot value expressions, Automated Transactions, Lot notes, Transactions @section Lot value expressions Normally when you ask Ledger to display the values of commodities held, it @@ -3291,8 +3338,8 @@ In most cases, it is simplest to either use explicit amounts in your valuation expressions, or just pass the arguments down to market after modifying them to suit your needs. -@node Automated transactions, , Lot value expressions, Transactions -@section Automated transactions +@node Automated Transactions, , Lot value expressions, Transactions +@section Automated Transactions An automated transaction is a special kind of transaction which adds its postings to other transactions any time one of that other transactions' @@ -3344,9 +3391,10 @@ transaction. * State flags:: * Effective Dates:: * Periodic Transactions:: +* Concrete Example of Automated Transactions:: @end menu -@node Amount multipliers, Accessing the matching posting's amount, Automated transactions, Automated transactions +@node Amount multipliers, Accessing the matching posting's amount, Automated Transactions, Automated Transactions @subsection Amount multipliers As a special case, if an automated transaction's posting's amount (phew) has @@ -3375,7 +3423,7 @@ Then the latter transaction turns into this during parsing: Bar $-1000.00 @end smallexample -@node Accessing the matching posting's amount, Referring to the matching posting's account, Amount multipliers, Automated transactions +@node Accessing the matching posting's amount, Referring to the matching posting's account, Amount multipliers, Automated Transactions @subsection Accessing the matching posting's amount If you use an amount expression for an automated transaction's posting, that @@ -3402,7 +3450,7 @@ This becomes: (Foo) $-40.00 @end smallexample -@node Referring to the matching posting's account, Applying metadata to every matched posting, Accessing the matching posting's amount, Automated transactions +@node Referring to the matching posting's account, Applying metadata to every matched posting, Accessing the matching posting's amount, Automated Transactions @subsection Referring to the matching posting's account Sometimes want to refer to the account that matched in some way within the @@ -3427,7 +3475,7 @@ Becomes: Assets:Cash $-20.00 @end smallexample -@node Applying metadata to every matched posting, Applying metadata to the generated posting, Referring to the matching posting's account, Automated transactions +@node Applying metadata to every matched posting, Applying metadata to the generated posting, Referring to the matching posting's account, Automated Transactions @subsection Applying metadata to every matched posting If the automated transaction has a transaction note, that note is copied @@ -3453,7 +3501,7 @@ Becomes: Assets:Cash $-20.00 @end smallexample -@node Applying metadata to the generated posting, State flags, Applying metadata to every matched posting, Automated transactions +@node Applying metadata to the generated posting, State flags, Applying metadata to every matched posting, Automated Transactions @subsection Applying metadata to the generated posting If the automated transaction's posting has a note, that note is carried to the @@ -3483,14 +3531,14 @@ This is slightly different from the rules for regular transaction notes, in that an automated transaction's note does not apply to every posting within the automated transaction itself, but rather to every posting it matches. -@node State flags, Effective Dates, Applying metadata to the generated posting, Automated transactions +@node State flags, Effective Dates, Applying metadata to the generated posting, Automated Transactions @subsection State flags Although you cannot mark an automated transaction as a whole as cleared or pending, you can mark its postings with a * or ! before the account name, and that state flag gets carried to the generated posting. -@node Effective Dates, Periodic Transactions, State flags, Automated transactions +@node Effective Dates, Periodic Transactions, State flags, Automated Transactions @subsection Effective Dates @cindex effective dates @@ -3557,16 +3605,93 @@ automatic $37.50 deficit like you should, while your checking account really knows that it debited $225 this month. -@node Periodic Transactions, , Effective Dates, Automated transactions +@node Periodic Transactions, Concrete Example of Automated Transactions, Effective Dates, Automated Transactions @subsection Periodic Transactions A periodic transaction starts with a ~ followed by a period expression. Periodic transactions are used for budgeting and forecasting only, they -have no effect without the @samp{--budget} option specified. +have no effect without the @code{--budget} option specified. See @ref{Budgeting and Forecasting} for examples and details. +@node Concrete Example of Automated Transactions, , Periodic Transactions, Automated Transactions +@subsection Concrete Example of Automated Transactions + + +As a Bahá'í, I need to compute Huqúqu'lláh whenever I acquire assets. +It is similar to tithing for Jews and Christians, or to Zakát for +Muslims. The exact details of computing Huqúqu'lláh are somewhat +complex, but if you have further interest, please consult the Web. + +Ledger makes this otherwise difficult law very easy. Just set up an +automated posting at the top of your ledger file: + +@smallexample +; This automated transaction will compute Huqúqu'lláh based on this +; journal's postings. Any that match will affect the +; Liabilities:Huququ'llah account by 19% of the value of that posting. + += /^(?:Income:|Expenses:(?:Business|Rent$|Furnishings|Taxes|Insurance))/ + (Liabilities:Huququ'llah) 0.19 +@end smallexample + +This automated posting works by looking at each posting in the +ledger file. If any match the given value expression, 19% of the +posting's value is applied to the @samp{Liabilities:Huququ'llah} +account. So, if $1000 is earned from @samp{Income:Salary}, $190 is +added to @samp{Liabilities:Huqúqu'lláh}; if $1000 is spent on Rent, +$190 is subtracted. The ultimate balance of Huqúqu'lláh reflects how +much is owed in order to fulfill one's obligation to Huqúqu'lláh. +When ready to pay, just write a check to cover the amount shown in +@samp{Liabilities:Huququ'llah}. That transaction would look like: + +@smallexample +2003/01/01 (101) Baha'i Huqúqu'lláh Trust + Liabilities:Huququ'llah $1,000.00 + Assets:Checking +@end smallexample + +That's it. To see how much Huqúq is currently owed based on your +ledger transactions, use: + +@smallexample +ledger balance Liabilities:Huquq +@end smallexample + +This works fine, but omits one aspect of the law: that Huquq is only +due once the liability exceeds the value of 19 mithqáls of gold (which +is roughly 2.22 ounces). So what we want is for the liability to +appear in the balance report only when it exceeds the present day +value of 2.22 ounces of gold. This can be accomplished using the +command: + +@smallexample +ledger -Q -t "/Liab.*Huquq/?(a/P@{2.22 AU@}<=@{-1.0@}&a):a" -s bal liab +@end smallexample + +With this command, the current price for gold is downloaded, and the +Huqúqu'lláh is reported only if its value exceeds that of 2.22 ounces +of gold. If you wish the liability to be reflected in the parent +subtotal either way, use this instead: + +@smallexample +ledger -Q -T "/Liab.*Huquq/?(O/P@{2.22 AU@}<=@{-1.0@}&O):O" -s bal liab +@end smallexample + +In some cases, you may wish to refer to the account of whichever +posting matched your automated transaction's value expression. To do +this, use the special account name @samp{$account}: + +@smallexample += /^Some:Long:Account:Name/ + [$account] -0.10 + [Savings] 0.10 +@end smallexample + +This example causes 10% of the matching account's total to be deferred +to the @samp{Savings} account---as a balanced virtual posting, +which may be excluded from reports by using @option{--real}. @@ -3640,7 +3765,7 @@ command. $ 5,480.00 20:39:21 ~/ledger/test/input > @end smallexample -@noindent note the implicit logical and between @samp{Auto} and @samp{Mastercard}. +@noindent note the implicit logical and between @code{Auto} and @code{Mastercard}. If you want the entire contents of a branch of your account tree, use the highest common name in the branch: @@ -3663,7 +3788,7 @@ You can use general regular expressions in nearly anyplace Ledger needs a string The first example looks for any account starting with ``Bo'', of which there are none. The second looks for any account with ``Bo'', which is -@samp{Expenses:Books}. +@code{Expenses:Books}. @cindex limit by payees If you want to know exactly how much you have spent in a particular @@ -3714,22 +3839,22 @@ The following query makes it easy to see monthly expenses, with each month's expenses sorted by the amount: @example -ledger -M --period-sort t reg ^expenses +ledger -M --period-sort "(amount)" reg ^expenses @end example Now, you might wonder where the money came from to pay for these -things. To see that report, add @option{-r}, which shows the +things. To see that report, add @code{-r}, which shows the ``related account'' postings: @example -ledger -M --period-sort t -r reg ^expenses +ledger -M --period-sort "(amount)" -r reg ^expenses @end example But maybe this prints too much information. You might just want to see how much you're spending with your MasterCard. That kind of query requires the use of a display predicate, since the postings -calculated must match @samp{^expenses}, while the postings -displayed must match @samp{mastercard}. The command would be: +calculated must match @code{^expenses}, while the postings +displayed must match @code{mastercard}. The command would be: @example ledger -M -r --display "account =~ /mastercard/" reg ^expenses @@ -3737,8 +3862,8 @@ ledger -M -r --display "account =~ /mastercard/" reg ^expenses This query says: Report monthly subtotals; report the ``related account'' postings; display only related postings whose -account matches @samp{mastercard}, and base the calculation on -postings matching @samp{^expenses}. +account matches @code{mastercard}, and base the calculation on +postings matching @code{^expenses}. This works just as well for report the overall total, too: @@ -3746,7 +3871,7 @@ This works just as well for report the overall total, too: ledger -s -r --display "account =~ /mastercard/"/ reg ^expenses @end example -The @option{-s} option subtotals all postings, just as @option{-M} +The @code{-s} option subtotals all postings, just as @code{-M} subtotaled by the month. The running total in both cases is off, however, since a display expression is being used. @@ -3882,8 +4007,8 @@ register reports. The script to do this is included in the ledger distribution, and is named @file{contrib/report}. Install @file{report} anywhere along your @env{PATH}, and then use @command{report} instead of @command{ledger} when doing a register report. The only thing to keep -in mind is that you must specify @option{-j (--amount-data)} or -@option{-J (--total-data)} to indicate whether Gnuplot should plot the +in mind is that you must specify @code{-j (--amount-data)} or +@code{-J (--total-data)} to indicate whether Gnuplot should plot the amount, or the running total. For example, this command plots total monthly expenses made on your MasterCard. @@ -3914,8 +4039,8 @@ report -J -l "Ua>=@{\$0.01@}" reg ^assets ^liab report -J -l "Ua>=@{\$0.01@}" -d "d>=[last feb]" reg ^assets ^liab @end smallexample -The last report uses both a calculation predicate (@option{-l}) and a -display predicate (@option{-d}). The calculation predicates limits +The last report uses both a calculation predicate (@code{-l}) and a +display predicate (@code{-d}). The calculation predicates limits the report to postings whose amount is greater than $1 (which can only happen if the posting amount is in dollars). The display predicate limits the transactions @emph{displayed} to just those since last @@ -3978,7 +4103,7 @@ always be the same as the current balance of that account. If you have Gnuplot installed, you may plot the amount or running total of any register by using the script @file{report}, which is included in the Ledger distribution. The only requirement is that you -add either @option{-j} or @option{-J} to your register command, in +add either @code{-j} or @code{-J} to your register command, in order to plot either the amount or total column, respectively. @node The print Command, , The register Command, Primary Financial Reports @@ -3998,14 +4123,14 @@ file whose formatting has gotten out of hand. @section Reports in other Formats @menu * Comma Separated Variable files:: -* Emacs:: -* Emacs org mode:: +* The lisp command:: +* EMACS org mode:: * The pricemap Command:: * The xml Command:: * prices and pricedb:: @end menu -@node Comma Separated Variable files, Emacs, Reports in other Formats, Reports in other Formats +@node Comma Separated Variable files, The lisp command, Reports in other Formats, Reports in other Formats @subsection Comma Separated Variable files @menu * The csv command:: @@ -4014,19 +4139,19 @@ file whose formatting has gotten out of hand. @node The csv command, The convert command, Comma Separated Variable files, Comma Separated Variable files @subsubsection The @code{csv} command -The csv command will output print out the desired ledger transactions in -a csv format suitable for import into other programs. You can determine -the transaction to print using all the normal limiting and searching +The @command{csv} command will output print out the desired ledger transactions in +a csv format suitable for import into other programs. You can specify +the transactions to print using all the normal limiting and searching functions. @cindex csv conversion @cindex reading csv @cindex comma separated variable file reading @node The convert command, , The csv command, Comma Separated Variable files @subsubsection The @code{convert} command -Convert reads your Ledger journal then parses a comma separated value -(csv) file into Ledger transactions. Many banks offer csv file -downloads. Unfortunately the file formats, aside form the commas, are -all different. The ledger convert command tried to help as much as it +The @code{convert} command parses a comma separated value +(csv) file and outputs Ledger transactions. Many banks offer csv file +downloads. Unfortunately the file formats, aside the from commas, are +all different. The ledger @code{convert} command tries to help as much as it can. Your banks csv files will have fields in different orders from other @@ -4058,7 +4183,7 @@ line of the file. The fields ledger can recognize are called Delete the account description lines at the top, and replace the first line in the data above with: @smallexample -date,payee,note,amount,,,code, +,date,payee,note,amount,,,code, @end smallexample Then execute ledger like this: @@ -4070,11 +4195,54 @@ Where the @code{--input-date-format} option tells ledger how to interpret the dates. Importing csv files is a lot of work, and but is very amenable to scripting. -@node Emacs, Emacs org mode, Comma Separated Variable files, Reports in other Formats -@subsection Emacs -The @command{emacs} command outputs results in a form that can be read -directly by Emacs Lisp. The format of the @code{sexp} is: +If there are columns in the bank data you would like to keep in your +ledger data, besides the primary fields described above, you can name +them in the field descriptor list and Ledger will include them in the +transaction as meta data if it doesn't recognize the field name. For +example, if you want to capture the bank transaction number and it +occurs in the first column of the data use: + + +@smallexample +transid,date,payee,note,amount,,,code, +@end smallexample + +Ledger will include @code{; transid: 767718} in the first transaction is +from the file above. + +The @code{convert} command accepts three options, the most important +ones are @code{--invert} which inverts the amount field, and +@code{--account NAME} which you can use to specify the account to +balance against and @code{--rich-data}. When using the rich-data switch +additional metadata is stored as tags. There is, for example, a UUID field. If +an entry with the same UUID tag is already included in the normal ledger +file (specified via @code{-f} or @code{$LEDGER_FILE}) this entry will not be printed +again. + +You can also use @code{convert} with @code{payee} and @code{account} +directives. First, you can use the @code{payee} and @code{alias} +directive to rewrite the @code{payee} field based on some rules. Then you can +use the account and its @code{payee} directive to specify the account. I use it +like this, for example: + +@smallexample +payee Aldi + alias ^ALDI SUED SAGT DANKE +account Aufwand:Einkauf:Lebensmittel + payee ^(Aldi|Alnatura|Kaufland|REWE)$ +@end smallexample + +Note that it may be necessary for the output of @code{ledger convert} to be +passed through @code{ledger print} a second time if you want to match on the +new payee field. During the @code{ledger convert} run only the original payee +name as specified in the csv data seems to be used. + +@node The lisp command, EMACS org mode, Comma Separated Variable files, Reports in other Formats +@subsection The @code{lisp} command + +The @command{lisp} command outputs results in a form that can be read +directly by EMACS Lisp. The format of the @code{sexp} is: @smallexample ((BEG-POS CLEARED DATE CODE PAYEE @@ -4082,24 +4250,26 @@ directly by Emacs Lisp. The format of the @code{sexp} is: ...) ; list of transactions @end smallexample -@node Emacs org mode, The pricemap Command, Emacs, Reports in other Formats -@subsection Emacs @code{org} Mode +@noindent @code{emacs} can also be used as a synonym for @code{lisp} + +@node EMACS org mode, The pricemap Command, The lisp command, Reports in other Formats +@subsection EMACS @code{org} Mode The @code{org} command produces a journal file suitable for use in the -Emacs org mode. More details on using org mode can be found at +EMACS org mode. More details on using org mode can be found at @url{http://www.orgmode.org}. -Org mode has a sub-system known as babel which allows for literate +Org mode has a sub-system known as Babel which allows for literate programming. This allows you to mix text and code within the same document and automatically execute code which may generate results which will then appear in the text. -One of the languages supported by org+babel is ledger so that you can +One of the languages supported by @code{org+babel} is Ledger, so that you can have ledger commands embedded in a text file and have the output of ledger commands also appear in the text file. The output can be updated whenever any new ledger entries are added. For instance, the following org mode text document snippet illustrates a -very naive but still useful of the org+babel system: +very naive but still useful of the @code{org+babel} system: @smallexample * A simple test of ledger in an org file @@ -4233,16 +4403,16 @@ Evaluating the code block again would generate a different report. Having to change the actual directive on the code block and re-evaluate makes it difficult to have more than one view of your transactions and financial state. Eventually, babel will support passing arguments to -#+call evaluations of code blocks but this support is missing +@code{#+call} evaluations of code blocks but this support is missing currently. Instead, we can use the concepts of literary programming, as implemented by the noweb features of babel, to help us. -@subsubheading Multiple Ledger source blocks with noweb +@subsubheading Multiple Ledger source blocks with @command{noweb} -The noweb feature of babel allows us to expand references to other code -blocks within a code block. For Ledger, this can be used to group -transactions according to type, say, and then bring various sets of -transactions together to generate reports. +The @command{noweb} feature of babel allows us to expand references to +other code blocks within a code block. For Ledger, this can be used to +group transactions according to type, say, and then bring various sets +of transactions together to generate reports. Using the same transactions used above, we could consider splitting these into expenses and income, as follows: @@ -4294,7 +4464,7 @@ have been done individually. Given the ledger entries defined above in the income and expenses code blocks, we can now refer to these using the noweb expansion directives, -<<name>>. We can now define different code blocks to generate specific +@code{<<name>>}. We can now define different code blocks to generate specific reports for those transactions. Below are two examples, one to generate a balance report and one to generate a register report of all transactions. @@ -4302,9 +4472,9 @@ transactions. The overall balance of your account and expenditure with a breakdown according to category is specified by passing the :cmdline bal argument -to Ledger. This code block can now be evaluated (C-c C-c) and the +to Ledger. This code block can now be evaluated (@code{C-c C-c}) and the results generated by incorporating the transactions referred to by the -<<income>> and <<expenses>>= lines. +@code{<<income>>} and @code{<<expenses>>} lines. @smallexample #+name: balance #+begin_src ledger :cmdline bal :noweb yes @@ -4319,7 +4489,7 @@ results generated by incorporating the transactions referred to by the @end smallexample If you want a more detailed breakdown of where your money is and where -it has been spent, you can specify the -s flag (i.e. :cmdline -s bal) to +it has been spent, you can specify the @code{-s} flag (i.e. @code{:cmdline -s bal}) to tell Ledger to include sub-accounts in the report. @smallexample @@ -4344,7 +4514,7 @@ tell Ledger to include sub-accounts in the report. You can also generate a monthly register (the reg command) by executing the following src block. This presents a summary of transactions for -each monthly period (the -M argument) with a running total in the final +each monthly period (the @code{-M} argument) with a running total in the final column (which should be 0 at the end if all the entries are correct). @smallexample @@ -4392,9 +4562,17 @@ file and manipulated using Babel. However, only simple Ledger features have been illustrated; please refer to the Ledger documentation for examples of more complex operations with a ledger. -@node The pricemap Command, The xml Command, Emacs org mode, Reports in other Formats +@node The pricemap Command, The xml Command, EMACS org mode, Reports in other Formats @subsection The @code{pricemap} Command +If you have the @code{graphviz} graph visualization package installed, ledger +can generate a graph of the relationship between your various +commodities. The output file is in the ``dot'' format. + +This is probably not very interesting, unless you have many different +commodities valued in terms of each other. For example, multiple +currencies and multiples investments valued in those currencies. + @node The xml Command, prices and pricedb, The pricemap Command, Reports in other Formats @subsection The @code{xml} Command @@ -4414,8 +4592,8 @@ The general format used for Ledger data is: </ledger> @end smallexample -The data stream is enclosed in a @samp{ledger} tag, which contains a -series of one or more transactions. Each @samp{xact} describes the +The data stream is enclosed in a @code{ledger} tag, which contains a +series of one or more transactions. Each @code{xact} describes the transaction and contains a series of one or more postings: @smallexample @@ -4432,19 +4610,19 @@ transaction and contains a series of one or more postings: </xact> @end smallexample -The date format for @samp{en:date} is always @samp{YYYY/MM/DD}. The -@samp{en:cleared} tag is optional, and indicates whether the posting has -been cleared or not. There is also an @samp{en:pending} tag, for -marking pending postings. The @samp{en:code} and @samp{en:payee} tags +The date format for @code{en:date} is always @code{YYYY/MM/DD}. The +@code{en:cleared} tag is optional, and indicates whether the posting has +been cleared or not. There is also an @code{en:pending} tag, for +marking pending postings. The @code{en:code} and @code{en:payee} tags both contain whatever text the user wishes. After the initial transaction data, there must follow a set of postings -marked with @samp{en:postings}. Typically these postings will all +marked with @code{en:postings}. Typically these postings will all balance each other, but if not they will be automatically balanced into -an account named @samp{<Unknown>}. +an account named @code{<Unknown>}. -Within the @samp{en:postings} tag is a series of one or more -@samp{posting}'s, which have the following form: +Within the @code{en:postings} tag is a series of one or more +@code{posting}'s, which have the following form: @smallexample <posting> @@ -4461,13 +4639,13 @@ Within the @samp{en:postings} tag is a series of one or more @end smallexample This is a basic posting. It may also be begin with -@samp{tr:virtual} and/or @samp{tr:generated} tags, to indicate virtual -and auto-generated postings. Then follows the @samp{tr:account} +@code{tr:virtual} and/or @code{tr:generated} tags, to indicate virtual +and auto-generated postings. Then follows the @code{tr:account} tag, which contains the full name of the account the posting is related to. Colons separate parent from child in an account name. Lastly follows the amount of the posting, indicated by -@samp{tr:amount}. Within this tag is a @samp{value} tag, of which +@code{tr:amount}. Within this tag is a @code{value} tag, of which there are four different kinds, each with its own format: @enumerate @@ -4477,15 +4655,15 @@ there are four different kinds, each with its own format: @item balance @end enumerate -The format of a Boolean value is @samp{true} or @samp{false} -surrounded by a @samp{boolean} tag, for example: +The format of a Boolean value is @code{true} or @code{false} +surrounded by a @code{boolean} tag, for example: @smallexample <boolean>true</boolean> @end smallexample The format of an integer value is the numerical value surrounded by an -@samp{integer} tag, for example: +@code{integer} tag, for example: @smallexample <integer>12036</integer> @@ -4547,11 +4725,11 @@ the same data. @node prices and pricedb, , The xml Command, Reports in other Formats -@subsection prices and pricedb +@subsection @code{prices} and @code{pricedb} The @command{prices} command displays the price history for matching -commodities. The @option{-A} flag is useful with this report, to -display the running average price, or @option{-D} to show each price's +commodities. The @code{-A} flag is useful with this report, to +display the running average price, or @code{-D} to show each price's deviation from that average. There is also a @command{pricedb} command which outputs the same @@ -4571,7 +4749,7 @@ database files. @end menu @node accounts, commodities, Reports about your Journals, Reports about your Journals -@subsection accounts +@subsection @code{accounts} The @command{accounts} reports all of the accounts in the journal. Following the command with a regular expression will limit the output to @@ -4579,16 +4757,16 @@ accounts matching the regex. @node commodities, entry and xact, accounts, Reports about your Journals -@subsection commodities +@subsection @command{commodities} Report all commodities present in the journals under consideration. @node entry and xact, payees, commodities, Reports about your Journals -@subsection entry and xact +@subsection @command{draft}, @command{entry} and @command{xact} -The @code{entry} and @command{xact} commands simplify the creation of -new transactions. It works on the principle that 80% of all postings -are variants of earlier postings. Here's how it works: +The @code{draft}, @code{entry} and @command{xact} commands simplify the +creation of new transactions. It works on the principle that 80% of all +postings are variants of earlier postings. Here's how it works: Say you currently have this posting in your ledger file: @@ -4599,7 +4777,7 @@ Say you currently have this posting in your ledger file: Liabilities:MasterCard $-15.00 @end smallexample -Now it's @samp{2004/4/9}, and you've just eating at @samp{Viva +Now it's @code{2004/4/9}, and you've just eating at @code{Viva Italiano} again. The exact amounts are different, but the overall form is the same. With the @command{xact} command you can type: @@ -4617,10 +4795,10 @@ This produces the following output: @end smallexample It works by finding a past posting matching the regular expression -@samp{viva}, and assuming that any accounts or amounts specified will +@code{viva}, and assuming that any accounts or amounts specified will be similar to that earlier posting. If Ledger does not succeed in generating a new transaction, an error is printed and the exit code is set -to @samp{1}. +to @code{1}. Here are a few more examples of the @command{xact} command, assuming the above journal transaction: @@ -4638,7 +4816,7 @@ ledger xact 4/9 viva dining "DM 11.50" backwards compatibility with Ledger 2.X. @node payees, , entry and xact, Reports about your Journals -@subsection payees +@subsection @code{payees} The @command{payees} reports all of the unique payees in the journal. To filter the payees displayed you must use the prefix: @smallexample @@ -4663,18 +4841,18 @@ macbook-2:$ @end menu @node echo, reload, Developer Commands, Developer Commands -@subsection echo +@subsection @command{echo} This command simply echos its argument back to the output. @node reload, source, echo, Developer Commands -@subsection reload +@subsection @command{reload} Forces ledger to reload any journal files. This function exists to support external programs controlling a running ledger process and does nothing for a command line user. @node source, Debug Options, reload, Developer Commands -@subsection source +@subsection @command{source} The @code{source} command take a journal file as an argument and parses it checking for errors, no other reports are generated, and no other arguments are necessary. Ledger will return success if no errors are @@ -4686,16 +4864,19 @@ found. These options are primarily for Ledger developers, but may be of some use to a user trying something new. - @option{--args-only} ignore init +@table @code + @item --args-only +ignore init files and environment variables for the ledger run. -@option{--verify} enable additional assertions during run-time. This -causes a significant slowdown. When combined with @option{--debug} -ledger will produce memory trace information. +@item --verify +enable additional assertions during run-time. This causes a significant +slowdown. When combined with @code{--debug} ledger will produce +memory trace information. -@option{--debug "argument"} If Ledger has been built with debug options -this will provide extra data during the run. The following are the -available arguments to debug: +@item --debug "argument" +If Ledger has been built with debug options this will provide extra data +during the run. The following are the available arguments to debug: @multitable @columnfractions .32 .43 .27 @item @code{account.display} @tab @code{expr.calc.when} @tab @code{org.next_amount} @@ -4724,7 +4905,7 @@ available arguments to debug: @item @code{expr.calc} @tab @code{option.names} @end multitable -@option{--trace INTEGER_TRACE_LEVEL} +@item --trace INTEGER_TRACE_LEVEL Enable tracing. The integer specifies the level of trace desired: @multitable @columnfractions .3 .7 @item @code{LOG_OFF} @tab 0 @@ -4741,15 +4922,16 @@ Enable tracing. The integer specifies the level of trace desired: @item @code{LOG_ALL} @tab 11 @end multitable -@option{--verbose} +@item --verbose Print detailed information on the execution of Ledger. -@option{--version} +@item --version Print version information and exit. +@end table @node Pre-commands, , Debug Options, Developer Commands @subsection Pre-Commands -Pre-commands are useful when you aren't sure how a command or option +Pre-commands are useful when you aren't sure how a command or option will work. @table @code @item args @@ -4773,6 +4955,8 @@ it against a model transaction. Print details of how ledger uses the given formatting description and apply it against a model transaction. @item generate +Randomly generates syntactically valid Ledger data from a seed. Used by the +GenerateTests harness for development testing @item parse <VALUE EXPR> Print details of how ledger uses the given value expression description and apply it against a model transaction. @@ -4808,7 +4992,7 @@ model transaction: --- Context is first posting of the following transaction --- 2004/05/27 Book Store ; This note applies to all postings. :SecondTag: - Expenses:Books 20 BOOK @ $10 + Expenses:Books 20 BOOK @@ $10 ; Metadata: Some Value ; Typed:: $100 + $200 ; :ExampleTag: @@ -4836,6 +5020,8 @@ model transaction: true @end smallexample @item template +Shows the insertion template that a @code{draft} or @code{xact} sub-command generates. +This is a debugging command. @end table @node Command-line Syntax, Budgeting and Forecasting, Reporting Commands, Top @@ -4844,11 +5030,12 @@ true @menu * Basic Usage:: +* Command Line Quick Reference:: * Detailed Options Description:: * Period Expressions:: @end menu -@node Basic Usage, Detailed Options Description, Command-line Syntax, Command-line Syntax +@node Basic Usage, Command Line Quick Reference, Command-line Syntax, Command-line Syntax @section Basic Usage This chapter describes Ledger's features and options. You may wish to @@ -4872,7 +5059,7 @@ meaning, described below. The regular expressions arguments always match the account name that a posting refers to. To match on the payee of the transaction instead, -precede the regular expression with @samp{payee} or @@. For example, the +precede the regular expression with @code{payee} or @@. For example, the following balance command reports account totals for rent, food and movies, but only those whose payee matches Freddie: @@ -4889,8 +5076,148 @@ There are many, many command options available with the However, none of them are required to use the basic reporting commands. +@node Command Line Quick Reference, Detailed Options Description, Basic Usage, Command-line Syntax +@section Command Line Quick Reference -@node Detailed Options Description, Period Expressions, Basic Usage, Command-line Syntax +@menu +* Reporting Commands Quick Reference:: +* Basic Options Quick Reference:: +* Report Filtering Quick Reference:: +* Error Checking and Calculation Options:: +* Output Customization Quick Reference:: +* Grouping Options:: +* Commodity Reporting Quick Reference:: +@end menu + +@node Reporting Commands Quick Reference, Basic Options Quick Reference, Command Line Quick Reference, Command Line Quick Reference +@subsection Reporting Commands +@multitable @columnfractions .2 .8 +@item @strong{Report} @tab @strong{Description} +@item @code{balance} @tab Show account balances +@item @code{register} @tab Show all transactions with running total +@item @code{csv} @tab Show transactions in csv format, for exporting to other programs +@item @code{print} @tab Print transaction in a ledger readable format +@item @code{xml} @tab Produce XML output of the register command +@item @code{emacs} @tab Produce EMACS lisp output +@item @code{equity} @tab Print account balances as transactions +@item @code{prices} @tab Print price history for matching commodities +@item @code{pricedb} @tab Print price history for matching commodities in ledger readable format +@item @code{xact} @tab Used to generate transactions based on previous postings +@end multitable + +@node Basic Options Quick Reference, Report Filtering Quick Reference, Reporting Commands Quick Reference, Command Line Quick Reference +@subsection Basic Options +@multitable @columnfractions .1 .25 .65 +@item @strong{Short} @tab @strong{Long} @tab @strong{Description} +@item @code{-h} @tab @code{--help} @tab prints summary of all options +@item @code{-v} @tab @code{--version} @tab prints version of ledger executable +@item @code{-f FILE} @tab @code{--file FILE} @tab read @file{FILE} as a ledger file +@item @code{-o FILE} @tab @code{--output FILE} @tab redirects output to @file{FILE} +@item @code{-i FILE} @tab @code{--init-file FILE} @tab specify options file +@item @code{-a NAME} @tab @code{--account NAME} @tab specify default account name for QIF file postings +@end multitable + +@node Report Filtering Quick Reference, Error Checking and Calculation Options, Basic Options Quick Reference, Command Line Quick Reference +@subsection Report Filtering +@multitable @columnfractions .1 .25 .65 +@item @strong{Short} @tab @strong{Long} @tab @strong{Description} +@item @code{-c} @tab @code{--current} @tab Display transaction on or before the current date +@item @code{-b DATE} @tab @code{--begin DATE} @tab Begin reports on or after @code{DATE} +@item @code{-e DATE} @tab @code{--end DATE} @tab Limits end date of transactions for report +@item @code{-p STR} @tab @code{--period} @tab Set report period to STR +@item @code{ } @tab @code{--period-sort} @tab Sort postings within each period +@item @code{-C} @tab @code{--cleared} @tab Display only cleared postings +@item @code{} @tab @code{--dc} @tab Display register or balance in debit/credit format +@item @code{-U} @tab @code{--uncleared} @tab Display only uncleared postings +@item @code{-R} @tab @code{--real} @tab Display only real postings +@item @code{-L} @tab @code{--actual} @tab Displays only actual postings, not automated +@item @code{-r} @tab @code{--related} @tab Display related postings +@item @code{} @tab @code{--budget} @tab Display how close your postings meet your budget +@item @code{} @tab @code{--add-budget} @tab Shows un-budgeted postings +@item @code{} @tab @code{--unbudgeted} @tab Shows only un-budgeted postings +@item @code{} @tab @code{--forecast} @tab Project balances into the future +@item @code{-l EXPR} @tab @code{--limit EXPR} @tab Limits postings in calculations +@item @code{-t EXPR} @tab @code{--amount} @tab Change value expression reported in register report +@item @code{-T EXPR} @tab @code{--total} @tab Change the value expression used for ``totals'' column in register and balance reports +@end multitable + +@node Error Checking and Calculation Options, Output Customization Quick Reference, Report Filtering Quick Reference, Command Line Quick Reference +@subsection Error Checking and Calculation Options + +@multitable @columnfractions .1 .25 .65 +@item @strong{Short} @tab @strong{Long} @tab @strong{Description} +@item @code{} @tab @code{--strict} @tab accounts, tags or commodities not previously declared will cause warnings +@item @code{} @tab @code{--pedantic} @tab accounts, tags or commodities not previously declared will cause errors +@item @code{} @tab @code{--check-payees} @tab enable strict and pedantic checking for payees as well as accounts, commodities and tags. +@item @code{} @tab @code{--immediate} @tab instructs ledger to evaluate calculations immediately rather than lazily +@end multitable + + +@node Output Customization Quick Reference, Grouping Options, Error Checking and Calculation Options, Command Line Quick Reference +@subsection Output Customization +@multitable @columnfractions .15 .4 .45 +@item @strong{Short} @tab @strong{Long} @tab @strong{Description} +@item @code{-n} @tab @code{--collapse} @tab Collapse transactions with multiple postings +@item @code{-s} @tab @code{--subtotal} @tab Report register as a single subtotal +@item @code{-P} @tab @code{--by-payee} @tab Report subtotals by payee +@item @code{-E} @tab @code{--empty} @tab Include empty accounts in report +@item @code{-W} @tab @code{--weekly} @tab Report posting totals by week +@item @code{-Y} @tab @code{--yearly} @tab Report posting totals by year +@item @code{} @tab @code{--dow} @tab report Posting totals by day of week +@item @code{-S EXPR} @tab @code{--sort EXPR} @tab Sorts a report using @code{EXPR} +@item @code{-w} @tab @code{--wide} @tab Assume 132 columns instead of 80 +@item @code{} @tab @code{--head N} @tab Report the first N postings +@item @code{} @tab @code{--tail N} @tab Report the last N postings +@item @code{} @tab @code{--pager PATH} @tab Direct output to @code{PATH} pager program +@item @code{-A} @tab @code{--average} @tab Reports average posting value +@item @code{-D} @tab @code{--deviation} @tab Reports each posting deviation from the average +@item @code{-%} @tab @code{--percent} @tab Show subtotals in the balance report as percentages +@c @item @code{} @tab @code{--totals} @tab Include running total in the @code{xml} report +@item @code{} @tab @code{--pivot TAG} @tab produce a pivot table of the tag type specified +@item @code{-j} @tab @code{--amount-data} @tab Show only date and value column to format the output for plots +@item @tab @code{--plot-amount-format STR} @tab specify the format for the plot output +@item @code{-J} @tab @code{--total-data} @tab Show only dates and totals to format the output for plots +@item @tab @code{--plot-total-format STR} @tab specify the format for the plot output +@item @code{-d EXPR} @tab @code{--display EXPR} @tab Display only posting that meet the criteris in the EXPR +@item @code{-y STR} @tab @code{--date-format STR} @tab Change the basic date format used in reports +@item @code{-F STR} @tab @code{--format STR} @tab Set reporting format +@item @code{} @tab @code{--balance-format STR} @tab +@item @code{} @tab @code{--register-format STR} @tab +@item @code{} @tab @code{--prices-format STR} @tab +@item @code{-w register} @tab @code{--wide-register-format STR} @tab +@item @code{} @tab @code{--anon} @tab Print the ledger register with anonymized accounts and payees, useful for filing bug reports +@end multitable + +@node Grouping Options, Commodity Reporting Quick Reference, Output Customization Quick Reference, Command Line Quick Reference +@subsection Grouping Options +@multitable @columnfractions .1 .25 .65 +@item @strong{Short} @tab @strong{Long} @tab @strong{Description} +@item @code{-P} @tab @code{--by-payee} @tab Group postings by common payee names +@item @code{-D} @tab @code{--daily} @tab Group postings by day +@item @code{-W} @tab @code{--weekly} @tab Group postings by week +@item @code{-M} @tab @code{--monthly} @tab Group postings by month +@item @code{} @tab @code{--quarterly} @tab Group postings by quarter +@item @code{-Y} @tab @code{--yearly} @tab Group postings by year +@item @code{} @tab @code{--dow} @tab Group by day of weeks +@item @code{-s} @tab @code{--subtotal} @tab Group posting together, similar to balance report +@end multitable + +@node Commodity Reporting Quick Reference, , Grouping Options, Command Line Quick Reference +@subsection Commodity Reporting + +@multitable @columnfractions .1 .25 .65 +@item @strong{Short} @tab @strong{Long} @tab @strong{Description} +@item @code{} @tab @code{--price-db FILE} @tab Use @file{FILE} for retrieving stored commodity prices +@item @code{-L MINS} @tab @code{--price-exp MINS} @tab Set expected freshness of prices in minutes +@item @code{-Q} @tab @code{--download} @tab Download quotes using @code{getquote} +@item @code{} @tab @code{--getquote} @tab Sets path to a user defined script to download commodity prices. +@item @code{-O} @tab @code{--quantity} @tab Report commodity totals without conversion +@item @code{-B} @tab @code{--basis} @tab Report cost basis +@item @code{-V} @tab @code{--market} @tab Report last known market value +@item @code{-G} @tab @code{--gain} @tab Report net gain loss for commodities that have a price history +@end multitable + +@node Detailed Options Description, Period Expressions, Command Line Quick Reference, Command-line Syntax @section Detailed Option Description @menu @@ -4898,7 +5225,6 @@ commands. * Session Options:: * Report Options:: * Report Filtering:: -* Search Terms:: * Output Customization:: * Commodity Reporting:: * Environment Variables:: @@ -4914,58 +5240,60 @@ GUIs, which would make use of the different scopes by keeping an instance of Ledger running in the background and running multiple sessions with multiple reports per session. - -@option{--args-only} Ignore all environment and init-file settings and +@table @code +@item --args-only +Ignore all environment and init-file settings and use only command-line arguments to control Ledger. Useful for debugs or testing small Journal files not associated with you main financial database. - -@option{--help} +@item --help Displays the info page for ledger. -@option{--init-file <PATH>} -Specifies the location of the init file @file{.ledgerrc} +@item --init-file <PATH> +Specifies the location of the init file. The default is @file{~/.ledgerrc} -@option{--options} Display the options in effect for this Ledger -invocation, along with their values and the source of those values, for -example: +@item --options + Display the options in effect for this Ledger invocation, along with +their values and the source of those values, for example: @smallexample 14:15:02 > ledger --options bal --cleared -f ~/ledger/test/input/drewr3.dat =============================================================================== [Global scope options] [Session scope options] - --file = ~/ledger/test/input/drewr3.dat -f - --price-db = ~/FinanceData/PriceDB $price-db + --file = ~/ledger/test/input/drewr3.dat -f + --price-db = ~/FinanceData/PriceDB $price-db [Report scope options] - --cleared --cleared - --color ?normalize - --date-format = %Y/%m/%d $date-format - --limit = cleared --cleared - --prepend-width = 0 ?normalize - --meta-width = 0 ?normalize - --date-width = 10 ?normalize - --payee-width = 21 ?normalize - --account-width = 21 ?normalize - --amount-width = 12 ?normalize - --total-width = 12 ?normalize + --cleared --cleared + --color ?normalize + --date-format = %Y/%m/%d $date-format + --limit = cleared --cleared + --prepend-width = 0 ?normalize + --meta-width = 0 ?normalize + --date-width = 10 ?normalize + --payee-width = 21 ?normalize + --account-width = 21 ?normalize + --amount-width = 12 ?normalize + --total-width = 12 ?normalize =============================================================================== $ 775.00 Assets:Checking $ -1,000.00 Equity:Opening Balances $ 225.00 Expenses:Food:Groceries -------------------- 0 - @end smallexample -@noindent For the `source' column, a value starting with a `@code{-}' or -`@code{--}' indicated the source was a command line argument. It the -entry starts with a `@code{$}', the source was an environment -variable. If the source is `@code{?normalize}' the value was set +@noindent For the source column, a value starting with a @code{-} or +@code{--} indicated the source was a command line argument. It the +entry starts with a @code{$}, the source was an environment +variable. If the source is @code{?normalize} the value was set internally by ledger, in a function called @code{normalize_options}. -@option{--script <PATH>} Execute a ledger script. +@item --script <PATH> +Execute a ledger script. +@end table + @node Session Options, Report Options, Global Options, Detailed Options Description @subsection Session Options @@ -4976,22 +5304,26 @@ GUIs, which would make use of the different scopes by keeping an instance of Ledger running in the background and running multiple sessions with multiple reports per session. -@option{--decimal-comma} Direct Ledger to parse journals using the -European standard comma as decimal separator, vice a period. +@table @code +@item --decimal-comma +Direct Ledger to parse journals using the European standard comma as +decimal separator, vice a period. -@option{--download} Direct Ledger to download prices using the script -defined in @code{--getquote}. +@item --download +Direct Ledger to download prices using the script defined in +@code{--getquote}. -@option{--file <PATH>} +@item --file <PATH> Specify the input file path for this invocation. @cindex getquote @cindex download prices -@option{--getquote <PATH>} Tells ledger where to find the user defined -script to download prices information. +@item --getquote <PATH> +Tells ledger where to find the user defined script to download prices +information. -@option{--input-date-format <DATE-FORMAT>} Specify the input date format -for journal entries. For example, +@item --input-date-format <DATE-FORMAT> +Specify the input date format for journal entries. For example, @smallexample ledger convert Export.csv --input-date-format "%m/%d/%Y" @end smallexample @@ -5000,8 +5332,8 @@ Would convert the @file{Export.csv} file to ledger format, assuming the the dates in the CSV file are like 12/23/2009 (@pxref{Date and Time Format Codes}). -@option{--master-account <ARGUMENT>} Prepends all account names with the -argument. +@item --master-account <STRING> +Prepends all account names with the argument. @smallexample 21:51:39 ~/ledger (next)> ledger -f test/input/drewr3.dat bal --master-account HUMBUG 0 HUMBUG @@ -5026,23 +5358,24 @@ argument. $ 200.00 Mortgage:Principal @end smallexample -@option{--price-db <PATH>} Specify the location of the price entry data -file. - -@option{--price-exp INTEGER_MINUTES} Set the expected freshness of price -quotes, in minutes. That is, if the last known quote for any commodity -is older than this value, and if ‘--download’ is being used, then the -Internet will be consulted again for a newer price. Otherwise, the old -price is still considered to be fresh enough. +@item --price-db <PATH> +Specify the location of the price entry data file. -@option{--strict} Ledger normally silently accepts any account or -commodity in a posting, even if you have misspelled a common used one. -The option @code{--strict} changes that behavior. While running -@code{--strict}, Ledger interprets all cleared transactions as correct, -and if it finds a new account or commodity (same as a misspelled -commodity or account) it will issue a warning giving you the file and -line number of the problem. +@item --price-exp INTEGER_MINUTES +Set the expected freshness of price quotes, in minutes. That is, if the +last known quote for any commodity is older than this value, and if +@code{--download} is being used, then the Internet will be consulted again +for a newer price. Otherwise, the old price is still considered to be +fresh enough. +@item --strict +Ledger normally silently accepts any account or commodity in a posting, +even if you have misspelled a common used one. The option +@code{--strict} changes that behavior. While running @code{--strict}, +Ledger interprets all cleared transactions as correct, and if it finds a +new account or commodity (same as a misspelled commodity or account) it +will issue a warning giving you the file and line number of the problem. +@end table @node Report Options, Report Filtering, Session Options, Detailed Options Description @subsection Report Options Options for Ledger report affect three separate scopes of operation: @@ -5051,48 +5384,61 @@ difference between these scopes. Ledger 3.0 contains provisions for GUIs, which would make use of the different scopes by keeping an instance of Ledger running in the background and running multiple sessions with multiple reports per session. -@option{--abbrev-len <INT>} Sets the minimum + +@table @code +@item --abbrev-len <INT> +Sets the minimum length an account can be abbreviated to if it doesn't fit inside the @code{account-width}. If @code{abbrev-len} is zero, then the account name will be truncated on the right. If @code{abbrev-len} is greater than @code{account-width} then the account will be truncated on the left, with no shortening of the account names in order to fit into the desired width. - -@option{--account <STR>} Prepend @code{<STR>} to all accounts +@item --account <STR> +Prepend @code{<STR>} to all accounts reported. That is, the option @code{--account Personal} would tack @code{Personal:} to the beginning of every account reported in a balance report or register report. -@option{--account-width <INT>} Set the width of the account column in +@item --account-width <INT> + Set the width of the account column in the @code{register} report to @code{N} characters. -@option{--actual-dates} Show actual dates of transactions +@item --actual-dates + Show actual dates of transactions (@pxref{Effective Dates}). Also @code{-L}. -@option{--actual} Report only real transactions, with no automated or +@item --actual + Report only real transactions, with no automated or virtual transactions used. -@option{--add-budget} Show only unbudgeted postings. +@item --add-budget + Show only unbudgeted postings. -@option{--amount-data} On a register report print only the dates and +@item --amount-data + On a register report print only the dates and amount of postings. Useful for graphing and spreadsheet applications. -@option{--amount <EXPR>} Apply the given value expression to the posting +@item --amount <EXPR> + Apply the given value expression to the posting amount (@pxref{Value Expressions}). Using @code{--amount} you can apply an arbitrary transformation to the postings. -@option{--amount-width <INT>} Set the width in characters of the amount +@item --amount-width <INT> + Set the width in characters of the amount column in the register report. -@option{--anon} anonymizes registry output, mostly for sending in bug +@item --anon + anonymizes registry output, mostly for sending in bug reports. -@option{--average} Print average values over the number of transactions +@item --average + Print average values over the number of transactions instead of running totals. -@option{--balance-format <STR>} specifies the format to use for the +@item --balance-format <STR> + specifies the format to use for the @code{balance} report (@pxref{Format Strings}). The default is: @smallexample "%(justify(scrub(display_total), 20, -1, true, color))" @@ -5102,14 +5448,18 @@ instead of running totals. "--------------------\n" @end smallexample -@option{--base} ASK JOHN +@item --base + ASK JOHN -@option{--basis} Report the cost basis on all posting +@item --basis + Report the cost basis on all posting -@option{--begin <DATE>} Specify the start date of all calculations. +@item --begin <DATE> + Specify the start date of all calculations. Transactions before that date will be ignored. -@option{--bold-if <EXPR>} print the entire line in bold if the given +@item --bold-if <EXPR> + print the entire line in bold if the given value expression is true (@pxref{Value Expressions}). @smallexample @@ -5117,7 +5467,8 @@ ledger reg Expenses --begin Dec --bold-if "amount > 100" @end smallexample @noindent list all transactions since the beginning of December and bold any posting greater than $100 -@option{--budget-format <FORMAT_STRING>} +@item --budget-format <FORMAT_STRING> + specifies the format to use for the @code{budget} report (@pxref{Format Strings}). The default is: @smallexample "%(justify(scrub(display_total), 20, -1, true, color))" @@ -5127,14 +5478,17 @@ specifies the format to use for the @code{budget} report (@pxref{Format Strings} "--------------------\n" @end smallexample -@option{--budget} only display budgeted items. In a register report this +@item --budget + only display budgeted items. In a register report this displays transaction in the budget, in a balance report this displays accounts in the budget (@pxref{Budgeting and Forecasting}). -@option{--by-payee <REGEXP>} +@item --by-payee <REGEXP> + group the register report by payee. -@option{--cleared-format <FORMAT_STRING>} specifies the format to use +@item --cleared-format <FORMAT_STRING> + specifies the format to use for the @code{cleared} report (@pxref{Format Strings}). The default is: @smallexample @@ -5149,25 +5503,32 @@ for the @code{cleared} report (@pxref{Format Strings}). The default is: "---------------- ---------------- ---------\n" @end smallexample -@option{--cleared} consider only transaction that have been cleared for +@item --cleared + consider only transaction that have been cleared for display and calculation. -@option{--collapse} By default ledger prints all account in an account +@item --collapse + By default ledger prints all account in an account tree. With @code{--collapse} it print only the top level account specified. -@option{--collapse-if-zero} Collapses the account display only if it has +@item --collapse-if-zero + Collapses the account display only if it has a zero balance. -@option{--color} use color is the tty supports it. +@item --color + use color is the tty supports it. -@option{--columns <INT>} specify the width of the register report in +@item --columns <INT> + specify the width of the register report in characters. -@option{--count} Direct ledger to report the number of items when +@item --count + Direct ledger to report the number of items when appended to the commodities, accounts or payees command. -@option{--csv-format} specifies the format to use for the @code{csv} +@item --csv-format + specifies the format to use for the @code{csv} report (@pxref{Format Strings}). The default is: @smallexample "%(quoted(date))," @@ -5179,26 +5540,33 @@ report (@pxref{Format Strings}). The default is: "%(quoted(cleared ? \"*\" : (pending ? \"!\" : \"\")))," "%(quoted(join(note | xact.note)))\n" @end smallexample -@option{--current} +@item --current + Shorthand for @code{--limit "date <= today"} -@option{--daily} +@item --daily + Shorthand for @code{--period "daily"} -@option{--date-format <DATE-FORMAT>} specifies format ledger should use +@item --date-format <DATE-FORMAT> + specifies format ledger should use to print dates (@pxref{Date and Time Format Codes}). -@option{--date <EXPR>} transforms the date of the transaction using +@item --date <EXPR> + transforms the date of the transaction using @code{EXPR} -@option{--date-width <INT>} specifies the width, in characters, of the +@item --date-width <INT> + specifies the width, in characters, of the date column in the register report. -@option{--datetime-format} +@item --datetime-format + ASK JOHN -@option{--dc} Display register or balance in debit/credit format -If you use @samp{--dc} with either the register (reg) or balance (bal) commands, you +@item --dc + Display register or balance in debit/credit format +If you use @code{--dc} with either the register (reg) or balance (bal) commands, you will now get extra columns. The register goes from this: @smallexample 12-Mar-10 Employer Assets:Cash $100 $100 @@ -5227,7 +5595,7 @@ will now get extra columns. The register goes from this: @noindent Where the first column is debits, the second is credits, and the third is the running total. Only the running total may contain negative values. -For the balance report without @samp{--dc}: +For the balance report without @code{--dc}: @smallexample $70 Assets:Cash @@ -5237,7 +5605,7 @@ For the balance report without @samp{--dc}: 0 @end smallexample -@noindent And with @samp{--dc} it becomes this: +@noindent And with @code{--dc} it becomes this: @smallexample $105 $35 $70 Assets:Cash @@ -5248,81 +5616,102 @@ For the balance report without @samp{--dc}: @end smallexample -@option{--depth <INT>} limit the depth of the account tree. In a balance -report, for example, a @code{--depth 2} statement will print balances -only for account with two levels, i.e. @code{Expenses:Entertainment} but -not @code{Expenses:entertainemnt:Dining}. This is a display predicate, -which means it only affects display, not the total calculations. +@item --depth <INT> + limit the depth of the account tree. In a balance report, for example, +a @code{--depth 2} statement will print balances only for account with +two levels, i.e. @code{Expenses:Entertainment} but not +@code{Expenses:entertainemnt:Dining}. This is a display predicate, which +means it only affects display, not the total calculations. -@option{--deviation} reports each posting’s deviation from the - average. It is only mean- ingful in the register and prices reports. +@item --deviation + reports each posting’s deviation from the average. It is only + meaningful in the register and prices reports. -@option{--display-amount <EXPR>} apply a transform to the +@item --display-amount <EXPR> + apply a transform to the @strong{displayed} amount. This occurs after calculations occur. -@option{--display <BOOLEAN_EXPR>} +@item --display <BOOLEAN_EXPR> + display lines that satisfy the expression given. -@option{--display-total <EXPR>} apply a transform to the +@item --display-total <EXPR> + apply a transform to the @strong{displayed} total. This occurs after calculations occur. -@option{--dow} +@item --dow + group transactions by the day of the week. @smallexample ledger reg Expenses --dow --collapse @end smallexample @noindent will print all Expenses totalled for each day of the week. -@option{--effective} +@item --effective + use effective dates for all calculations (@pxref{Effective Dates}). -@option{--empty} +@item --empty + include empty accounts in the report. -@option{--end <DATE>} +@item --end <DATE> + specify the end date for transaction to be considered in the report. -@option{--equity} related to the @code{equity} command (@pxref{The +@item --equity + related to the @code{equity} command (@pxref{The equity Command}). Gives current account balances in the form of a register report. -@option{--exact} +@item --exact + ASK JOHN -@option{--exchange <COMMODITY>} display values in terms of the given +@item --exchange <COMMODITY> + display values in terms of the given commodity. The latest available price is used. -@option{--flat} force the full names of accounts to be used inthe +@item --flat + force the full names of accounts to be used in the balance report. The balance report will not use an indented tree. -@option{--force-color} output tty color codes even if the tty doesn't -support them. Ueful for TTY that don't advertise their capabilities +@item --force-color + output tty color codes even if the tty doesn't +support them. Useful for TTY that don't advertise their capabilities correctly. -@option{--force-pager} +@item --force-pager + force Ledger to paginate its output. -@option{forecast-while} -FIX THIS ENTRY +@item --forecast-while <VEXPR> +Continue forecasting while @code{<VEXPR>} is true. -@option{forecast-years} -FIX THIS ENTRY +@item --forecast-years <INT> +Forecast at most @code{N} years in the future. + +@item --format <FORMAT STRING> -@option{--format <FORMAT STRING>} use the given format string to print output. -@option{--gain} -report on gains using the latest available prices . +@item --gain -@option{generated} -ASK JOHN +report on gains using the latest available prices. -@option{--group-by <EXPR>} group transaction together in the register -report. EXPR can be anything, although most common would be -@code{"payee"} or @code{"commodity"}. The @code{tags()} function is +@item --generated +Include auto-generated postings (such as those from automated transactions) +in the report, in cases where you normally wouldn't want +them. + +@item --group-by <EXPR> + group transaction together in the register +report. @code{EXPR} can be anything, although most common would be +@code{payee} or @code{commodity}. The @code{tags()} function is also useful here. -@option{--group-title-format} sets the format for the headers that +@item --group-title-format + sets the format for the headers that separate reports section of a grouped report. Only has effect with a @code{--group-by} register report. @smallexample @@ -5339,200 +5728,285 @@ ledger reg Expenses --group-by "payee" --group-title-format "------------------- @end smallexample -@option{--head <INT>} -Print the first INT entries. Opposite of @code{--tail}. +@item --head <INT> + +Print the first @code{INT} entries. Opposite of @code{--tail}. + +@item --inject +Use @code{Expected} amounts in calculations. In the case that you know +that amount a transaction should be, but the actual transaction has the +wrong value you can use metadata to put in the expected amount: +@smallexample +2012-03-12 Paycheck + Income $-990; Expected:: $-1000.00 + Checking +@end smallexample -@option{--inject} -See email from John W. +Then using the command @code{ledger reg --inject=Expected Income} would +treat the transaction as if the ``Expected Value'' was actual. +@item --invert -@option{--invert} Change the sign of all reported values. -@option{--limit <EXPR>} Only transactions that satisfy the expression -will be considered in the calculation. +@item --limit <EXPR> + Only transactions that satisfy the expression will be considered in the +calculation. -@option{--lot-dates} -FIX THIS ENTRY +@item --lot-dates +Report the date on which each commodity in a balance report was purchased. -@option{--lot-prices} -FIX THIS ENTRY +@item --lot-prices -@option{--lot-tags} -FIX THIS ENTRY +Report the price at which each commodity in a balance report was purchased. -@option{--lots-actual} -FIX THIS ENTRY +@item --lot-tags -@option{--lots} -FIX THIS ENTRY +Report the tag attached to each commodity in a balance report. -@option{market} -FIX THIS ENTRY +@item --lots-actual -@option{meta} FIX THIS ENTRY -@option{meta-width} -FIX THIS ENTRY +@item --lots -@option{--monthly} -FIX THIS ENTRY +Report the date and price at which each commodity was purchased in a balance report. + +@item --market + +Use the latest market value for all commodities. + +@item --meta <TAG> + +In the register report, prepend the transaction with the value of the given tag. + +@item --meta-width + +Specify the width of the Meta column used for the @code{--meta} options. + +@item --monthly + +synonym for @code{--period "monthly"} + +@item --no-color -@option{--no-color} suppress any color TTY output. -@option{--no-rounding} -FIX THIS ENTRY +@item --no-rounding +Don't output <Rounding> postings. Note that this will cause the running total +to often not add up! It's main use is for @code{-j} and @code{-J} reports. -@option{--no-titles} -FIX THIS ENTRY +@item --no-titles -@option{--no-total} -FIX THIS ENTRY +Suppress the output of group titles -@option{--now} -FIX THIS ENTRY +@item --no-total -@option{only} -FIX THIS ENTRY +Suppress printing the final total line in a balance report. -@option{--output} -FIX THIS ENTRY +@item --now +Define the current date in case to you to do calculate in the past or +future using @code{--current} -@option{--pager} -FIX THIS ENTRY +@item --only -@option{payee} -FIX THIS ENTRY +This is a postings predicate that applies after certain transforms have +been executed, such as periodic gathering. -@option{payee-width} -FIX THIS ENTRY +@item --output <PATH> +Redirect the output of ledger to the file defined in @file{PATH}. -@option{--pending} -Use only postings tht are marked pending +@item --pager -@option{percent} -FIX THIS ENTRY +Specify the pager program to use. -@option{period} -FIX THIS ENTRY +@item --payee <VEXPR> -@option{--pivot} -FIX THIS ENTRY +Sets a value expression for formatting the payee. In the register report +this prevents the second entry from having a date and payee for each +transaction -@option{plot-amount-format} -FIX THIS ENTRY +@item --payee-width N -@option{plot-total-format} -FIX THIS ENTRY +Set the number of columns dedicated to the payee in the register report. -@option{prepend-format} -FIX THIS ENTRY +@item --pending -@option{prepend-width} -FIX THIS ENTRY +Use only postings that are marked pending -@option{pricedb-format} -FIX THIS ENTRY +@item --percent +Calculate the percentage value of each account in a balance reports. +Only works for account that have a single commodity. -@option{price} -FIX THIS ENTRY +@item --period <PERIOD EXPRESSION> -@option{prices-format} -FIX THIS ENTRY +Define a period expression the sets the time period during which +transactions are to be accounted. For a register report only the +transactions that satisfy the period expression with be displayed. For +a balance report only those transactions will be accounted in the final +balances. -@option{quantity} -FIX THIS ENTRY +@item --pivot <VALUED TAG> -@option{--quarterly} -FIX THIS ENTRY +Produce a balance pivot report ``around'' the given tag. For example, +if you have multiple cars and track each fuel purchase in +@code{Expenses:Auto:Fuel} and tag each fuel purchase with a tag +identifying which car the purchase was for @code{; Car: Prius}, then the command: +@smallexample +ledger bal Fuel --pivot "Car" --period "this year" + $ 3491.26 Car + $ 1084.22 M3:Expenses:Auto:Fuel + $ 149.65 MG V11:Expenses:Auto:Fuel + $ 621.89 Prius:Expenses:Auto:Fuel + $ 1635.50 Sienna:Expenses:Auto:Fuel + $ 42.69 Expenses:Auto:Fuel +-------------------- + $ 3533.95 +@end smallexample -@option{raw} -FIX THIS ENTRY +@xref{Metadata values}. +@item --plot-amount-format -@option{--real} Account using only real transactions ignoring virtual -and automatic transactions. +Define the output format for a amount data plot. @xref{Visualizing with Gnuplot}. -@option{register-format} -FIX THIS ENTRY +@item --plot-total-format + +Define the output format for a total data plot. @xref{Visualizing with Gnuplot}. + +@item --prepend-format STR + +Prepend STR to every line of the output + +@item --prepend-width N + +Reserve @code{N} spaces at the beginning of each line of the output -@option{related-all} -FIX THIS ENTRY -@option{--related} -In a register report show the related account. +@item --price +use the price of the commodity purchase for performing calculations + +@item --quantity -@option{--revalued-only} FIX THIS ENTRY -@option{--revalued-total} +@item --quarterly + +Synonym for @code{--period "quarterly"}. + +@item --raw + +In the print report, show transactions using the exact same syntax as +specified by the user in their data file. Don't do any massaging or +interpreting. Can be useful for minor cleanups, like just aligning +amounts. + +@item --real +Account using only real transactions ignoring virtual and automatic +transactions. + + +@item --related-all + +Show all postings in a transaction, similar to @code{--related} but show +both ``sides'' of each transaction. + +@item --related + +In a register report show the related account. This is the other +``side'' of the transaction. + +@item --revalued-only + FIX THIS ENTRY -@option{--revalued} +@item --revalued-total + FIX THIS ENTRY -@option{seed} +@item --revalued + FIX THIS ENTRY -@option{sort-all} +@item --seed + +Sets the random seed for the @code{generate} command. Used as part of development testing. + +@item --sort-all + FIX THIS ENTRY -@option{--sort <VEXPR>} +@item --sort <VEXPR> + Sort the register report based on the value expression given to sort -@option{--sort-xacts} -FIX THIS ENTRY +@item --sort-xacts <VEXPR> -@option{--start-of-week <INT>} Tell ledger to use a particular day of -the week to start its ``weekly'' summary. @code{--start-of-week=1} -specifies Monday as the start of the week. +Sort the posting within transactions using the given value expression -@option{--subtotal} -FIX THIS ENTRY +@item --start-of-week <INT> +Tell ledger to use a particular day of the week to start its ``weekly'' +summary. @code{--start-of-week=1} specifies Monday as the start of the +week. -@option{--tail <INT>} -report only the last <INT> entries. Only useful ona register report. +@item --subtotal -@option{total-data} FIX THIS ENTRY -@option{total} -FIX THIS ENTRY +@item --tail <INT> -@option{total-width} -FIX THIS ENTRY +report only the last @code{INT} entries. Only useful on a register report. -@option{truncate} -FIX THIS ENTRY +@item --total-data -@option{unbudgeted} FIX THIS ENTRY -@option{uncleared} -FIX THIS ENTRY +@item --total <VEXPR> +Define a value expression used to calculate the total in reports. + +@item --total-width +Set the width of the total field in the register report. + +@item --truncate +Indicates how truncation should happen when the contents of columns +exceed their width. Valid arguments are @code{leading}, @code{middle}, +and @code{trailing}. The default is smarter than any of these three, as +it considers sub-names within the account name (that style is called +``abbreviate''). + +@item --unbudgeted -@option{unrealized-gains} FIX THIS ENTRY -@option{unrealized-losses} +@item --uncleared + +Use only uncleared transactions in calculations and reports. + +@item --unrealized-gains + FIX THIS ENTRY -@option{unrealized} +@item --unrealized-losses + FIX THIS ENTRY -@option{unround} +@item --unrealized + FIX THIS ENTRY -@option{--weekly} -synonymn for @code{--period "weekly"} +@item --unround +Perform all calculations without rounding and display results to full precision. + +@item --weekly -@option{--wide} lets the register report use 132 columns. Identical to -@code{--columns "132"} +synonym for @code{--period "weekly"} -@option{yearly} -synonymn for @code{--period "yearly"} +@item --wide +lets the register report use 132 columns. Identical to @code{--columns +"132"} +@item --yearly +synonym for @code{--period "yearly"} +@end table @@ -5542,29 +6016,37 @@ These are the most basic command options. Most likely, the user will want to set them using environment variables (see @ref{Environment Variables}), instead of using actual command-line options: -@option{--help} (@option{-h}) prints a summary of all the options, and -what they are used for. This can be a handy way to remember which -options do what. This help screen is also printed if ledger is run -without a command. - -@option{--version} (@option{-v}) prints the current version of ledger -and exits. This is useful for sending bug reports, to let the author -know which version of ledger you are using. - -@option{--file FILE} (@option{-f FILE}) reads FILE as a ledger file. -This command may be used multiple times. -Typically, the environment variable -@env{LEDGER_FILE} is set, rather than using this command-line option. - -@option{--output FILE} (@option{-o FILE}) redirects output from any -command to @var{FILE}. By default, all output goes to standard -output. - -@option{--init-file FILE} (@option{-i FILE}) causes FILE to be read by -ledger before any other ledger file. This file may not contain any -postings, but it may contain option settings. To specify options -in the init file, use the same syntax as the command-line, but put each -option on it's own line. Here's an example init file: +@table @code +@item --help +@item -h +Prints a summary of all the options, and what they are used for. This +can be a handy way to remember which options do what. This help screen +is also printed if ledger is run without a command. + +@item --version +@item -v +prints the current version of ledger and exits. This is useful for +sending bug reports, to let the author know which version of ledger you +are using. + +@item --file FILE +@item -f FILE +reads FILE as a ledger file. This command may be used multiple times. +Typically, the environment variable @env{LEDGER_FILE} is set, rather +than using this command-line option. + +@item --output FILE +@item -o FILE +redirects output from any command to @var{FILE}. By default, all output +goes to standard output. + +@item --init-file FILE +@item -i FILE +causes @code{FILE} to be read by ledger before any other ledger file. This +file may not contain any postings, but it may contain option settings. +To specify options in the init file, use the same syntax as the +command-line, but put each option on it's own line. Here's an example +init file: @smallexample --price-db ~/finance/.pricedb @@ -5576,68 +6058,83 @@ Option settings on the command-line or in the environment always take precedence over settings in the init file. -@option{--account NAME} (@option{-a NAME}) specifies the default -account which QIF file postings are assumed to relate to. +@item --account NAME +@item -a NAME +specifies the default account which QIF file postings are assumed to +relate to. +@end table -@node Report Filtering, Search Terms, Report Options, Detailed Options Description +@node Report Filtering, Output Customization, Report Options, Detailed Options Description @subsection Report filtering These options change which postings affect the outcome of a report, in ways other than just using regular expressions: -@option{--current}(@option{-c}) displays only transactions occurring on or -before the current date. - -@option{--begin DATE} (@option{-b DATE}) constrains the report to -transactions on or after @var{DATE}. Only transactions after that date will be -calculated, which means that the running total in the balance report -will always start at zero with the first matching transaction. (Note: This -is different from using @option{--display} to constrain what is -displayed). - -@option{--end DATE} (@option{-e DATE}) constrains the report so that -transactions on or after @var{DATE} are not considered. The ending date -is inclusive. - -@option{--period STR} (@option{-p STR}) sets the reporting period -to @var{STR}. This will subtotal all matching transactions within each -period separately, making it easy to see weekly, monthly, quarterly, -etc., posting totals. A period string can even specify the -beginning and end of the report range, using simple terms like ``last -June'' or ``next month''. For more using period expressions, see -@ref{Period Expressions}. - -@option{--period-sort EXPR} sorts the postings within each -reporting period using the value expression @var{EXPR}. This is most -often useful when reporting monthly expenses, in order to view the -highest expense categories at the top of each month: +@table @code +@item --current +@item -c +displays only transactions occurring on or before the current date. + +@item --begin DATE +@item -b DATE +constrains the report to transactions on or after @var{DATE}. Only +transactions after that date will be calculated, which means that the +running total in the balance report will always start at zero with the +first matching transaction. (Note: This is different from using +@code{--display} to constrain what is displayed). + +@item --end DATE +@item -e DATE +constrains the report so that transactions on or after @var{DATE} are +not considered. The ending date is inclusive. + +@item --period STR +@item -p STR +sets the reporting period to @var{STR}. This will subtotal all matching +transactions within each period separately, making it easy to see +weekly, monthly, quarterly, etc., posting totals. A period string can +even specify the beginning and end of the report range, using simple +terms like ``last June'' or ``next month''. For more using period +expressions, see @ref{Period Expressions}. + +@item --period-sort EXPR +sorts the postings within each reporting period using the value +expression @var{EXPR}. This is most often useful when reporting monthly +expenses, in order to view the highest expense categories at the top of +each month: @smallexample ledger -M --period-sort -At reg ^Expenses @end smallexample -@option{--cleared} (@option{-C}) displays only postings whose transaction -has been marked ``cleared'' (by placing an asterisk to the right of the -date). +@item --cleared +@item -C + displays only postings whose transaction has been marked ``cleared'' +(by placing an asterisk to the right of the date). -@option{--uncleared} (@option{-U}) displays only postings whose -transaction has not been marked ``cleared'' (i.e., if there is no asterisk to -the right of the date). +@item --uncleared +@item -U +displays only postings whose transaction has not been marked ``cleared'' +(i.e., if there is no asterisk to the right of the date). -@option{--real} (@option{-R}) displays only real postings, not virtual. -(A virtual posting is indicated by surrounding the account name with -parentheses or brackets; see @ref{Virtual postings} for more -information). +@item --real +@item -R + displays only real postings, not virtual. (A virtual posting is +indicated by surrounding the account name with parentheses or brackets; +see @ref{Virtual postings} for more information). -@option{--actual} (@option{-L}) displays only actual postings, and -not those created due to automated postings. +@item --actual +@item -L +displays only actual postings, and not those created due to automated +postings. -@option{--related} (@option{-r}) displays postings that are -related to whichever postings would otherwise have matched the -filtering criteria. In the register report, this shows where money -went to, or the account it came from. In the balance report, it shows -all the accounts affected by transactions having a related posting. -For example, if a file had this transaction: +@item --related +@item -r +displays postings that are related to whichever postings would otherwise +have matched the filtering criteria. In the register report, this shows +where money went to, or the account it came from. In the balance +report, it shows all the accounts affected by transactions having a +related posting. For example, if a file had this transaction: @smallexample 2004/03/20 Safeway @@ -5660,154 +6157,184 @@ posting that matched: Assets:Checking $85.00 $65.00 @end smallexample -@option{--budget} is useful for displaying how close your postings -meet your budget. @option{--add-budget} also shows un-budgeted -postings, while @option{--unbudgeted} shows only those. -@option{--forecast} is a related option that projects your budget into -the future, showing how it will affect future balances. -@xref{Budgeting and Forecasting}. +@item --budget +is useful for displaying how close your postings meet your budget. +@code{--add-budget} also shows un-budgeted postings, while +@code{--unbudgeted} shows only those. @code{--forecast} is a related +option that projects your budget into the future, showing how it will +affect future balances. @xref{Budgeting and Forecasting}. -@option{--limit EXPR} (@option{-l EXPR}) limits which postings -take part in the calculations of a report. +@item --limit EXPR +@item -l EXPR +limits which postings take part in the calculations of a report. -@option{--amount EXPR} (@option{-t EXPR}) changes the value expression -used to calculate the ``value'' column in the @command{register} -report, the amount used to calculate account totals in the -@command{balance} report, and the values printed in the +@item --amount EXPR +@item -t EXPR +changes the value expression used to calculate the ``value'' column in +the @command{register} report, the amount used to calculate account +totals in the @command{balance} report, and the values printed in the @command{equity} report. @xref{Value Expressions}. -@option{--total EXPR} (@option{-T EXPR}) sets the value expression -used for the ``totals'' column in the @command{register} and -@command{balance} reports. - -@node Search Terms, Output Customization, Report Filtering, Detailed Options Description -@subsection Search Terms - -Valid Ledger invocations look like: -@smallexample - ledger [OPTIONS] <COMMAND> <SEARCH-TERMS> -@end smallexample - -Where @samp{COMMAND} is any command verb (@pxref{Reporting Commands}), @samp{OPTIONS} can occur -anywhere, and @samp{SEARCH-TERM} is one or more of the following: - -@smallexample - word search for any account containing 'word' - TERM and TERM boolean AND between terms - TERM or TERM boolean OR between terms - not TERM invert the meaning of the term - payee word search for any payee containing 'word' - @@word shorthand for 'payee word' - desc word alternate for 'payee word' - note word search for any note containing 'word' - &word shorthand for 'note word' - tag word search for any metadata tag containing 'word' - tag word=value search for any metadata tag containing 'word' - whose value contains 'value' - %word shorthand for 'tag word' - %word=value shorthand for 'tag word=value' - meta word alternate for 'tag word' - meta word=value alternate for 'tag word=value' - expr 'EXPR' apply the given value expression as a predicate - '=EXPR' shorthand for 'expr EXPR' - \( TERMS \) group terms; useful if using and/or/not -@end smallexample - -So, to list all transaction that charged to ``food'' but not ``dining'' for any payee other than ``chang'' the following three commands would be equivalent: - -@smallexample - ledger reg food not dining @@chang - ledger reg food and not dining and not payee chang - ledger reg food not dining expr 'payee =~ /chang/' -@end smallexample - -@node Output Customization, Commodity Reporting, Search Terms, Detailed Options Description +@item --total EXPR +@item -T EXPR +sets the value expression used for the ``totals'' column in the +@command{register} and @command{balance} reports. +@end table +@c @node Search Terms, Output Customization, Report Filtering, Detailed Options Description +@c @subsection Search Terms + +@c Valid Ledger invocations look like: +@c @smallexample +@c ledger [OPTIONS] <COMMAND> <SEARCH-TERMS> +@c @end smallexample + +@c Where @code{COMMAND} is any command verb (@pxref{Reporting Commands}), @code{OPTIONS} can occur +@c anywhere, and @code{SEARCH-TERM} is one or more of the following: + +@c @smallexample +@c word search for any account containing 'word' +@c TERM and TERM boolean AND between terms +@c TERM or TERM boolean OR between terms +@c not TERM invert the meaning of the term +@c payee word search for any payee containing 'word' +@c @@word shorthand for 'payee word' +@c desc word alternate for 'payee word' +@c note word search for any note containing 'word' +@c &word shorthand for 'note word' +@c tag word search for any metadata tag containing 'word' +@c tag word=value search for any metadata tag containing 'word' +@c whose value contains 'value' +@c %word shorthand for 'tag word' +@c %word=value shorthand for 'tag word=value' +@c meta word alternate for 'tag word' +@c meta word=value alternate for 'tag word=value' +@c expr 'EXPR' apply the given value expression as a predicate +@c '=EXPR' shorthand for 'expr EXPR' +@c \( TERMS \) group terms; useful if using and/or/not +@c @end smallexample + +@c So, to list all transaction that charged to ``food'' but not ``dining'' +@c for any payee other than ``chang'' the following three commands would be +@c equivalent: + +@c @smallexample +@c ledger reg food not dining @@chang +@c ledger reg food and not dining and not payee chang +@c ledger reg food not dining expr 'payee =~ /chang/' +@c @end smallexample + +@node Output Customization, Commodity Reporting, Report Filtering, Detailed Options Description @subsection Output Customization These options affect only the output, but not which postings are used to create it: -@option{--collapse} (@option{-n}) causes transactions in a -@command{register} report with multiple postings to be collapsed +@table @code +@item --collapse +@item -n +causes transactions in a @command{register} report with multiple +postings to be collapsed into a single, subtotaled transaction. + +@item --subtotal +@item -s +causes all transactions in a @command{register} report to be collapsed into a single, subtotaled transaction. -@option{--subtotal} (@option{-s}) causes all transactions in a -@command{register} report to be collapsed into a single, subtotaled -transaction. - -@option{--by-payee} (@option{-P}) reports subtotals by payee. - - -@option{--empty} (@option{-E}) includes even empty accounts in the -@command{balance} report. - -@option{--weekly} (@option{-W}) reports posting totals by the -week. The week begins on whichever day of the week begins the month -containing that posting. To set a specific begin date, use a -period string, such as @samp{weekly from DATE}. @option{--monthly} -(@option{-M}) reports posting totals by month; @option{--yearly} -(@option{-Y}) reports posting totals by year. For more complex -period, using the @option{--period} option described above. - -@option{--dow} reports postings totals for each day of the week. -This is an easy way to see if weekend spending is more than on -weekdays. - -@option{--sort EXPR} (@option{-S EXPR}) sorts a report by comparing -the values determined using the value expression @var{EXPR}. For -example, using @option{-S -UT} in the balance report will sort account -balances from greatest to least, using the absolute value of the -total. For more on how to use value expressions, see @ref{Value -Expressions}. - -@option{--pivot TAG} produces a pivot table around the tag provided. -This requires meta data using valued tags. - -@option{--wide} (@option{-w}) causes the default @command{register} -report to assume 132 columns instead of 80. - -@option{--head} causes only the first N transactions to be printed. This -is different from using the command-line utility @command{head}, which -would limit to the first N postings. @option{--tail} outputs only -the last N transactions. Both options may be used simultaneously. If a -negative amount is given, it will invert the meaning of the flag -(instead of the first five transactions being printed, for example, it -would print all but the first five). - -@option{--pager} tells Ledger to pass its output to the given pager -program---very useful when the output is especially long. This -behavior can be made the default by setting the @env{LEDGER_PAGER} -environment variable. - -@option{--average} (@option{-A}) reports the average posting -value. - -@option{--deviation} (@option{-D}) reports each posting's -deviation from the average. It is only meaningful in the -@command{register} and @command{prices} reports. - -@option{--percent} (@option{-%}) shows account subtotals in the -@command{balance} report as percentages of the parent account. - -@c @option{--totals} include running total information in the +@item --by-payee +@item -P +reports subtotals by payee. + + +@item --empty +@item -E +includes even empty accounts in the @command{balance} report. + +@item --weekly +@item -W +reports posting totals by the week. The week begins on whichever day of +the week begins the month containing that posting. To set a specific +begin date, use a period string, such as @code{weekly from DATE}. +@item --monthly +@item -M +reports posting totals by month; +@item --yearly +@item -Y +reports posting totals by year. For more complex period, using the +@item --period +option described above. + +@item --dow +reports postings totals for each day of the week. This is an easy way +to see if weekend spending is more than on weekdays. + +@item --sort EXPR +@item -S EXPR +sorts a report by comparing the values determined using the value +expression @var{EXPR}. For example, using @code{-S -UT} in the +balance report will sort account balances from greatest to least, using +the absolute value of the total. For more on how to use value +expressions, see @ref{Value Expressions}. + +@item --pivot TAG +produces a pivot table around the tag provided. This requires meta data +using valued tags. + +@item --wide +@item -w +causes the default @command{register} report to assume 132 columns +instead of 80. + +@item --head +causes only the first @code{N} transactions to be printed. This is different +from using the command-line utility @command{head}, which would limit to +the first N postings. @code{--tail} outputs only the last @code{N} +transactions. Both options may be used simultaneously. If a negative +amount is given, it will invert the meaning of the flag (instead of the +first five transactions being printed, for example, it would print all +but the first five). + +@item --pager +tells Ledger to pass its output to the given pager program; very useful +when the output is especially long. This behavior can be made the +default by setting the @env{LEDGER_PAGER} environment variable. + +@item --average +@item -A +reports the average posting value. + +@item --deviation +@item -D +reports each posting's deviation from the average. It is only +meaningful in the @command{register} and @command{prices} reports. + +@item --percent +@item -% +shows account subtotals in the @command{balance} report as percentages +of the parent account. + +@c @code{--totals} include running total information in the @c @command{xml} report. -@option{--amount-data} (@option{-j}) changes the @command{register} -report so that it outputs nothing but the date and the value column, -and the latter without commodities. This is only meaningful if the -report uses a single commodity. This data can then be fed to other -programs, which could plot the date, analyze it, etc. +@item --amount-data +@item -j +changes the @command{register} report so that it outputs nothing but the +date and the value column, and the latter without commodities. This is +only meaningful if the report uses a single commodity. This data can +then be fed to other programs, which could plot the date, analyze it, +etc. -@option{--total-data} (@option{-J}) changes the @command{register} -report so that it outputs nothing but the date and totals column, -without commodities. +@item --total-data +@item -J +changes the @command{register} report so that it outputs nothing but the +date and totals column, without commodities. -@option{--display EXPR} (@option{-d EXPR}) limits which postings -or accounts or actually displayed in a report. They might still be -calculated, and be part of the running total of a register report, for -example, but they will not be displayed. This is useful for seeing -last month's checking postings, against a running balance which -includes all posting values: +@item --display EXPR +@item -d EXPR +limits which postings or accounts or actually displayed in a report. +They might still be calculated, and be part of the running total of a +register report, for example, but they will not be displayed. This is +useful for seeing last month's checking postings, against a running +balance which includes all posting values: @smallexample ledger -d "d>=[last month]" reg checking @@ -5822,41 +6349,136 @@ ledger -p "last month" reg checking @end smallexample Which is more useful depends on what you're looking to know: the total -amount for the reporting range (@option{-p}), or simply a display -restricted to the reporting range (using @option{-d}). - -@option{--date-format STR} (@option{-y STR}) changes the basic date -format used by reports. The default uses a date like 2004/08/01, -which represents the default date format of @samp{%Y/%m/%d}. To -change the way dates are printed in general, the easiest way is to put -@option{--date-format FORMAT} in the Ledger initialization file -@file{~/.ledgerrc} (or the file referred to by @env{LEDGER_INIT}). - -@option{--format STR} (@option{-F STR}) sets the reporting format for -whatever report ledger is about to make. @xref{Format Strings}. -There are also specific format commands for each report type: - -@itemize -@item @option{--balance-format STR} -@item @option{--register-format STR} -@item @option{--print-format STR} -@item @option{--plot-amount-format STR} (-j @command{register}) -@item @option{--plot-total-format STR} (-J @command{register}) -@item @option{--equity-format STR} -@item @option{--prices-format STR} -@item @option{--wide-register-format STR} (-w @command{register}) -@end itemize +amount for the reporting range (@code{-p}), or simply a display +restricted to the reporting range (using @code{-d}). + +@item --date-format STR +@item -y STR +changes the basic date format used by reports. The default uses a date +like @code{2004/08/01}, which represents the default date format of +@code{%Y/%m/%d}. To change the way dates are printed in general, the +easiest way is to put @code{--date-format FORMAT} in the Ledger +initialization file @file{~/.ledgerrc} (or the file referred to by +@env{LEDGER_INIT}). + +@item --format STR +@item -F STR +sets the reporting format for whatever report ledger is about to make. +@xref{Format Strings}. There are also specific format commands for each +report type: + +@item --balance-format STR +Define the output format for the @code{balance} report. The default (defined in @code{report.h} is: +@smallexample + "%(ansify_if( + justify(scrub(display_total), 20, + 20 + int(prepend_width), true, color), + bold if should_bold)) + %(!options.flat ? depth_spacer : \"\") + %-(ansify_if( + ansify_if(partial_account(options.flat), blue if color), + bold if should_bold))\n%/ + %$1\n%/ + %(prepend_width ? \" \" * int(prepend_width) : \"\") + --------------------\n" +@end smallexample +@item --cleared-format +Defines the format for the cleared report. The default is: +@smallexample + "%(justify(scrub(get_at(display_total, 0)), 16, 16 + int(prepend_width), + true, color)) %(justify(scrub(get_at(display_total, 1)), 18, + 36 + int(prepend_width), true, color)) + %(latest_cleared ? format_date(latest_cleared) : \" \") + %(!options.flat ? depth_spacer : \"\") + %-(ansify_if(partial_account(options.flat), blue if color))\n%/ + %$1 %$2 %$3\n%/ + %(prepend_width ? \" \" * int(prepend_width) : \"\") + ---------------- ---------------- ---------\n" +@end smallexample +@item --register-format STR +Define the output format for the @code{register} report. The default (defined in @code{report.h} is: +@smallexample + "%(ansify_if( + ansify_if(justify(format_date(date), int(date_width)), + green if color and date > today), + bold if should_bold)) + %(ansify_if( + ansify_if(justify(truncated(payee, int(payee_width)), int(payee_width)), + bold if color and !cleared and actual), + bold if should_bold)) + %(ansify_if( + ansify_if(justify(truncated(display_account, int(account_width), + int(abbrev_len)), int(account_width)), + blue if color), + bold if should_bold)) + %(ansify_if( + justify(scrub(display_amount), int(amount_width), + 3 + int(meta_width) + int(date_width) + int(payee_width) + + int(account_width) + int(amount_width) + int(prepend_width), + true, color), + bold if should_bold)) + %(ansify_if( + justify(scrub(display_total), int(total_width), + 4 + int(meta_width) + int(date_width) + int(payee_width) + + int(account_width) + int(amount_width) + int(total_width) + + int(prepend_width), true, color), + bold if should_bold))\n%/ + %(justify(\" \", int(date_width))) + %(ansify_if( + justify(truncated(has_tag(\"Payee\") ? payee : \" \", + int(payee_width)), int(payee_width)), + bold if should_bold)) + %$3 %$4 %$5\n" +@end smallexample +@item --csv-format +Sets the format for @code{csv} reports. The default is: +@smallexample +"%(quoted(date)), + %(quoted(code)), + %(quoted(payee)), + %(quoted(display_account)), + %(quoted(commodity)), + %(quoted(quantity(scrub(display_amount)))), + %(quoted(cleared ? \"*\" : (pending ? \"!\" : \"\"))), + %(quoted(join(note | xact.note)))\n" +@end smallexample +@item --plot-amount-format STR +Sets the format for amount plots, using the @code{-j} option. The default is: +@smallexample +"%(format_date(date, \"%Y-%m-%d\")) %(quantity(scrub(display_amount)))\n" +@end smallexample +@item --plot-total-format STR +Sets the format for total plots, using the @code{-J} option. The default is: +@smallexample +"%(format_date(date, \"%Y-%m-%d\")) %(quantity(scrub(display_total)))\n" +@end smallexample +@item --pricedb-format STR +Sets the format expected for the historical price file. The default is +@smallexample +"P %(datetime) %(display_account) %(scrub(display_amount))\n" +@end smallexample + +@item --prices-format STR +Sets the format for the @command{prices} report. The default is: +@smallexample +"%(date) %-8(display_account) %(justify(scrub(display_amount), 12, + 2 + 9 + 8 + 12, true, color))\n" +@end smallexample +@item --wide-register-format STR +(-w @command{register}) +@end table @node Commodity Reporting, Environment Variables, Output Customization, Detailed Options Description @subsection Commodity Reporting These options affect how commodity values are displayed: - -@option{--price-db FILE} sets the file that is used for recording -downloaded commodity prices. It is always read on start up, to -determine historical prices. Other settings can be placed in this -file manually, to prevent downloading quotes for a specific commodity, for -example. This is done by adding a line like the following: +@table @code +@item --price-db FILE +sets the file that is used for recording downloaded commodity prices. +It is always read on start up, to determine historical prices. Other +settings can be placed in this file manually, to prevent downloading +quotes for a specific commodity, for example. This is done by adding a +line like the following: @smallexample ; Don't download quotes for the dollar, or timelog values @@ -5864,27 +6486,34 @@ N $ N h @end smallexample -Note: Ledger NEVER write output to files. You are responsible for -updated the price-db file. The best way is to have your price download +@noindent Note: Ledger NEVER writes output to files. You are responsible for +updating the price-db file. The best way is to have your price download script maintain this file. -@option{--price-exp MINS} (@option{-L MINS}) sets the expected -freshness of price quotes, in minutes. That is, if the last known quote -for any commodity is older than this value---and if @option{--download} -is being used---then the Internet will be consulted again for a newer -price. Otherwise, the old price is still considered to be fresh enough. - -@option{--download} (@option{-Q}) causes quotes to be automagically -downloaded, as needed, by running a script named @command{getquote} -and expecting that script to return a value understood by ledger. A -sample implementation of a @command{getquote} script, implemented in -Perl, is provided in the distribution. Downloaded quote price are -then appended to the price database, usually specified using the -environment variable @env{LEDGER_PRICE_DB}. - +The format of the file can be changed by telling ledger to use the +@code{--pricedb-format} you define. + +@item --price-exp MINS +@item -L MINS +sets the expected freshness of price quotes, in minutes. That is, if +the last known quote for any commodity is older than this value, and if +@code{--download} is being used, then the Internet will be consulted +again for a newer price. Otherwise, the old price is still considered +to be fresh enough. + +@item --download +@item -Q +causes quotes to be automagically downloaded, as needed, by running a +script named @command{getquote} and expecting that script to return a +value understood by ledger. A sample implementation of a +@command{getquote} script, implemented in Perl, is provided in the +distribution. Downloaded quote price are then appended to the price +database, usually specified using the environment variable +@env{LEDGER_PRICE_DB}. +@end table There are several different ways that ledger can report the totals it displays. The most flexible way to adjust them is by using value -expressions, and the @option{-t} and @option{-T} options. However, +expressions, and the @code{-t} and @code{-T} options. However, there are also several ``default'' reports, which will satisfy most users basic reporting needs: @@ -5912,10 +6541,10 @@ commodity can mean different things to different people, depending on the accounts involved, the commodities, the nature of the transactions, etc. -When you specify @samp{-V}, or @samp{-X COMM}, you are requesting that +When you specify @code{-V}, or @code{-X COMM}, you are requesting that some or all of the commodities be valuated as of today (or whatever -@samp{--now} is set to). But what does such a valuation mean? This -meaning is governed by the presence of a @samp{VALUE} meta-data property, +@code{--now} is set to). But what does such a valuation mean? This +meaning is governed by the presence of a @code{VALUE} meta-data property, whose content is an expression used to compute that value. If no VALUE property is specified, each posting is assumed to have a @@ -5926,9 +6555,9 @@ follows: = expr true ; VALUE:: market(amount, date, exchange) @end smallexample -This definition emulates the present day behavior of @samp{-V} and @samp{-X} (in the -case of @samp{-X}, the requested commodity is passed via the string 'exchange' -above). +This definition emulates the present day behavior of @code{-V} and +@code{-X} (in the case of @code{-X}, the requested commodity is passed +via the string 'exchange' above). @cindex Euro conversion One thing many people have wanted to do is to fixate the valuation of @@ -5940,8 +6569,8 @@ old European currencies in terms of the Euro after a certain date: ; VALUE:: date < [Jun 2008] ? market(amount, date, exchange) : 1.44 EUR @end smallexample -This says: If @samp{--now} is some old date, use market prices as they -were at that time; but if @samp{--now} is past June 2008, use a fixed +This says: If @code{--now} is some old date, use market prices as they +were at that time; but if @code{--now} is past June 2008, use a fixed price for converting Deutsch Mark to Euro. Or how about never re-valuating commodities used in Expenses, since they @@ -5954,7 +6583,7 @@ cannot have a different future value: This says the future valuation is the same as the valuation at the time of posting. post.date equals the posting's date, while just 'date' is -the value of @samp{--now} (defaults to today). +the value of @code{--now} (defaults to today). Or how about valuating miles based on a reimbursement rate during a specific time period: @@ -5966,7 +6595,7 @@ specific time period: @end smallexample In this case, miles driven in 2007 will always be valuated at $1.05 -each. If you use @samp{-X EUR} to expressly request all amounts in +each. If you use @code{-X EUR} to expressly request all amounts in Euro, Ledger shall convert $1.05 to Euro by whatever means are appropriate for dollars. @@ -6006,16 +6635,16 @@ another currency. For example: Ledger presently has no way of handling such things as FIFO and LIFO. If you specify an unadorned commodity name, like AAPL, it will balance -against itself. If @samp{--lots} are not being displayed, then it will +against itself. If @code{--lots} are not being displayed, then it will appear to balance against any lot of AAPL. @cindex adorned commodity If you specify an adorned commodity, like AAPL @{$10.00@}, it will also -balance against itself, and against any AAPL if @samp{--lots} is not -specified. But if you do specify @samp{--lot-prices}, for example, then +balance against itself, and against any AAPL if @code{--lots} is not +specified. But if you do specify @code{--lot-prices}, for example, then it will balance against that specific price for AAPL. -Normally when you use @samp{-X <commodity>} to request that amounts be reported in a +Normally when you use @code{-X <commodity>} to request that amounts be reported in a specific commodity, Ledger uses these values: @itemize @@ -6028,16 +6657,17 @@ specific commodity, Ledger uses these values: For the balance report, use the value of that commodity as of today. @end itemize -You can now specify -H to ask that all valuations for any amount be done +You can now specify @code{-H} to ask that all valuations for any amount be done relative to the date that amount was encountered. -You can also now use -X (and -H) in conjunction with -B and -I, to see -valuation reports of just your basis costs or lot prices. +You can also now use @code{-X} (and @code{-H}) in conjunction with +@code{-B} and @code{-I}, to see valuation reports of just your basis +costs or lot prices. @node Environment Variables, , Commodity Reporting, Detailed Options Description @subsection Environment variables Every option to ledger may be set using an environment variable. If -an option has a long name such @option{--this-option}, setting the +an option has a long name such @code{--this-option}, setting the environment variable @env{LEDGER_THIS_OPTION} will have the same affect as specifying that option on the command-line. Options on the command-line always take precedence over environment variable @@ -6114,7 +6744,7 @@ last week The beginning and ending can be given at the same time, if it spans a single period. In that case, just use @var{SPEC} by itself. In that -case, the period @samp{oct}, for example, will cover all the days in +case, the period @code{oct}, for example, will cover all the days in October. The possible forms are: @smallexample @@ -6188,7 +6818,7 @@ ledger -p "this year" --monthly --average --subtotal balance ^expenses The reported totals are the current year's average for each account. Once these period transactions are defined, creating a budget report is as -easy as adding @option{--budget} to the command-line. For example, a +easy as adding @code{--budget} to the command-line. For example, a typical monthly expense report would be: @example @@ -6203,8 +6833,8 @@ ledger --budget --monthly register ^expenses A budget report includes only those accounts that appear in the budget. To see all expenses balanced against the budget, use -@option{--add-budget}. You can even see only the un-budgeted expenses -using @option{--unbudgeted}: +@code{--add-budget}. You can even see only the un-budgeted expenses +using @code{--unbudgeted}: @example ledger --unbudgeted --monthly register ^expenses @@ -6239,15 +6869,15 @@ ledger --forecast "d<[2010]" bal ^assets ^liabilities @node Value Expressions, Format Strings, Budgeting and Forecasting, Top @chapter Value Expressions -Value expressions are an expression language used by Ledger to -calculate values used by the program for many different purposes: +Ledger uses value expressions to make calculations for many different +purposes: @enumerate @item The values displayed in reports @item For predicates (where truth is anything non-zero), to determine which -postings are calculated (@option{-l}) or displayed (@option{-d}). +postings are calculated (@code{-l}) or displayed (@code{-d}). @item For sorting criteria, to yield the sort key. @item @@ -6255,19 +6885,21 @@ In the matching criteria used by automated postings. @end enumerate Value expressions support most simple math and logic operators, in -addition to a set of one letter functions and variables. A function's -argument is whatever follows it. The following is a display predicate -that I use with the @command{balance} command: +addition to a set of functions and variables. -@smallexample -ledger -d /^Liabilities/?T<0:UT>100 balance -@end smallexample +@c A function's +@c argument is whatever follows it. The following is a display predicate +@c that I use with the @command{balance} command: -The effect is that account totals are displayed only if: 1) A -Liabilities account has a total less than zero; or 2) the absolute -value of the account's total exceeds 100 units of whatever commodity -contains. If it contains multiple commodities, only one of them must -exceed 100 units. +@c @smallexample +@c ledger -d /^Liabilities/?T<0:UT>100 balance +@c @end smallexample + +@c The effect is that account totals are displayed only if: 1) A +@c Liabilities account has a total less than zero; or 2) the absolute +@c value of the account's total exceeds 100 units of whatever commodity +@c contains. If it contains multiple commodities, only one of them must +@c exceed 100 units. Display predicates are also very handy with register reports, to constrain which transactions are printed. For example, the following @@ -6279,8 +6911,8 @@ ledger -d "d>[this month]" register checking @end smallexample This advantage to this command's complexity is that it prints the -running total in terms of all transactions in the register. The following, -simpler command is similar, but totals only the displayed +running total in terms of all transactions in the register. The +following, simpler command is similar, but totals only the displayed postings: @smallexample @@ -6301,20 +6933,20 @@ Below are the one letter variables available in any value expression. For the register and print commands, these variables relate to individual postings, and sometimes the account affected by a posting. For the balance command, these variables relate to -accounts---often with a subtle difference in meaning. The use of each +accounts, often with a subtle difference in meaning. The use of each variable for both is specified. @table @code @item t -This maps to whatever the user specified with @option{-t}. In a -register report, @option{-t} changes the value column; in a balance -report, it has no meaning by default. If @option{-t} was not +This maps to whatever the user specified with @code{-t}. In a +register report, @code{-t} changes the value column; in a balance +report, it has no meaning by default. If @code{-t} was not specified, the current report style's value expression is used. @item T -This maps to whatever the user specified with @option{-T}. In a -register report, @option{-T} changes the totals column; in a balance -report, this is the value given for each account. If @option{-T} was +This maps to whatever the user specified with @code{-T}. In a +register report, @code{-T} changes the totals column; in a balance +report, this is the value given for each account. If @code{-T} was not specified, the current report style's value expression is used. @item m @@ -6341,7 +6973,7 @@ The market value of a posting, or an account without its children. @item g The net gain (market value minus cost basis), for a posting or an -account without its children. It is the same as @samp{v-b}. +account without its children. It is the same as @code{v-b}. @item l The depth (``level'') of an account. If an account has one parent, @@ -6383,7 +7015,7 @@ all its children. @item G The total net gain (market value minus cost basis), for a series of postings, or an account and its children. It is the same as -@samp{V-B}. +@code{V-B}. @end table @node Functions, Operators, Variables, Value Expressions @@ -6402,12 +7034,12 @@ The absolute (unsigned) value of the argument. Strips the commodity from the argument. @item A -The arithmetic mean of the argument; @samp{Ax} is the same as -@samp{x/n}. +The arithmetic mean of the argument; @code{Ax} is the same as +@code{x/n}. @item P -The present market value of the argument. The syntax @samp{P(x,d)} is -supported, which yields the market value at time @samp{d}. If no date +The present market value of the argument. The syntax @code{P(x,d)} is +supported, which yields the market value at time @code{d}. If no date is given, then the current moment is used. @end table @@ -6417,10 +7049,10 @@ is given, then the current moment is used. The binary and ternary operators, in order of precedence, are: @enumerate -@item @samp{* /} -@item @samp{+ -} -@item @samp{! < > =} -@item @samp{& | ?:} +@item @code{* /} +@item @code{+ -} +@item @code{! < > =} +@item @code{& | ?:} @end enumerate @menu @@ -6502,177 +7134,235 @@ precedence order of operators. @item [DATE] Useful specifying a date in plain terms. For example, you could say -@samp{[2004/06/01]}. +@code{[2004/06/01]}. @end table +@menu +* Misc:: +@end menu + +@node Misc, , Complex Expressions, Complex Expressions +@subsection Miscellaneous +@multitable @columnfractions .3 .2 .5 +@item @strong{Function} @tab @strong{Abbrev.} @tab @strong{Description} +@item @code{amount_expr } @tab @code{} @tab +@item @code{abs } @tab @code{} @tab --> U +@item @code{code} @tab @code{} @tab returns the transaction code, the string between the parenthesis after the date. +@item @code{commodity } @tab @code{} @tab +@item @code{display_amount } @tab @code{} @tab --> t +@item @code{display_total } @tab @code{} @tab --> T +@item @code{date } @tab @code{} @tab +@item @code{format_date } @tab @code{} @tab +@item @code{format } @tab @code{} @tab +@item @code{floor } @tab @code{} @tab +@item @code{get_at } @tab @code{} @tab +@item @code{is_seq } @tab @code{} @tab +@item @code{justify } @tab @code{} @tab +@item @code{join } @tab @code{} @tab +@item @code{market --> P } @tab @code{} @tab +@item @code{null } @tab @code{} @tab +@item @code{now --> d m } @tab @code{} @tab +@item @code{options } @tab @code{} @tab +@item @code{post } @tab @code{} @tab +@item @code{percent } @tab @code{} @tab +@item @code{price } @tab @code{} @tab +@item @code{print } @tab @code{} @tab +@item @code{quoted } @tab @code{} @tab +@item @code{quantity } @tab @code{} @tab +@item @code{rounded } @tab @code{} @tab +@item @code{scrub } @tab @code{} @tab +@item @code{strip --> S } @tab @code{} @tab +@item @code{should_bold } @tab @code{} @tab +@item @code{truncated } @tab @code{} @tab +@item @code{total_expr } @tab @code{} @tab +@item @code{today } @tab @code{} @tab +@item @code{top_amount } @tab @code{} @tab +@item @code{to_boolean } @tab @code{} @tab +@item @code{to_int } @tab @code{} @tab +@item @code{to_datetime } @tab @code{} @tab +@item @code{to_date } @tab @code{} @tab +@item @code{to_amount } @tab @code{} @tab +@item @code{to_balance } @tab @code{} @tab +@item @code{to_spring } @tab @code{} @tab +@item @code{to_mask } @tab @code{} @tab +@item @code{to_sequence } @tab @code{} @tab +@item @code{unrounded } @tab @code{} @tab +@item @code{value_date } @tab @code{} @tab +@end multitable @node Format Strings, Ledger for Developers, Value Expressions, Top @chapter Format Strings @menu * Basics:: +* Format String Structure:: * Format Expressions:: * --balance-format:: -* New formatting codes:: -* Date and Time Format Codes:: +* Formatting codes:: @end menu -@node Basics, Format Expressions, Format Strings, Format Strings +@node Basics, Format String Structure, Format Strings, Format Strings @section Format String Basics -Format strings may be used to change the output format of reports. -They are specified by passing a formatting string to the -@option{--format} (@option{-F}) option. Within that string, -constructs are allowed which make it possible to display the various -parts of an account or posting in custom ways. +Format strings may be used to change the output format of reports. They +are specified by passing a formatting string to the @code{--format} +(@code{-F}) option. Within that string, constructs are allowed which +make it possible to display the various parts of an account or posting +in custom ways. + +There are several additional flags that allow you to define formats for +specific reports. These are useful to define in your configuration file +and will allow you to run ledger reports from the command line without +having to enter a new format for each command. + +@itemize +@item @code{--balance-report} +@item @code{--cleared-report} +@item @code{--register-report} +@item @code{--csv-report} +@item @code{--plot-amount-report} +@item @code{--plot-total-report} +@item @code{--pricedb-report} +@item @code{--prices-report} +@item @code{--wide-register-report} +@end itemize +@node Format String Structure, Format Expressions, Basics, Format Strings +@section Format String Structure Within a format string, a substitution is specified using a percent -character (@samp{%}). The basic format of all substitutions is: +character (@code{%}). The basic format of all substitutions is: @smallexample %[-][MIN WIDTH][.MAX WIDTH](VALEXPR) @end smallexample -If the optional minus sign (@samp{-}) follows the percent character, +If the optional minus sign (@code{-}) follows the percent character, whatever is substituted will be left justified. The default is right -justified. If a minimum width is given next, the substituted text -will be at least that wide, perhaps wider. If a period and a maximum -width is given, the substituted text will never be wider than this, -and will be truncated to fit. Here are some examples: +justified. If a minimum width is given next, the substituted text will +be at least that wide, perhaps wider. If a period and a maximum width +is given, the substituted text will never be wider than this, and will +be truncated to fit. Here are some examples: -@smallexample -%-P a transaction's payee, left justified -%20P The same, right justified, at least 20 chars wide -%.20P The same, no more than 20 chars wide -%-.20P Left justified, maximum twenty chars wide -@end smallexample +@table @code +@item %-20P +a transaction's payee, left justified and padded to 20 characters wide. +@item %20P +The same, right justified, at least 20 chars wide +@item %.20P +The same, no more than 20 chars wide +@end table -The expression following the format constraints can be a single -letter, or an expression enclosed in parentheses or brackets. +The expression following the format constraints can be a single letter, +or an expression enclosed in parentheses or brackets. -@node Format Expressions, --balance-format, Basics, Format Strings +@node Format Expressions, --balance-format, Format String Structure, Format Strings @section Format Expressions - The -allowable expressions are: + The allowable expressions are: @table @code @item % Inserts a percent sign. @item t -Inserts the results of the value expression specified by @option{-t}. -If @option{-t} was not specified, the current report style's value +Inserts the results of the value expression specified by @code{-t}. +If @code{-t} was not specified, the current report style's value expression is used. @item T -Inserts the results of the value expression specified by @option{-T}. -If @option{-T} was not specified, the current report style's value +Inserts the results of the value expression specified by @code{-T}. +If @code{-T} was not specified, the current report style's value expression is used. -@item | -Inserts a single space. This is useful if a width is specified, for -inserting a certain number of spaces. - -@item _ -Inserts a space for each level of an account's depth. That is, if an -account has two parents, this construct will insert two spaces. If a -minimum width is specified, that much space is inserted for each level -of depth. Thus @samp{%5_}, for an account with four parents, will -insert twenty spaces. - @item (EXPR) Inserts the amount resulting from the value expression given in parentheses. To insert five times the total value of an account, for -example, one could say @samp{%12(5*O)}. Note: It's important to put -the five first in that expression, so that the commodity doesn't get +example, one could say @code{%12(5*O)}. Note: It's important to put the +five first in that expression, so that the commodity doesn't get stripped from the total. @item [DATEFMT] Inserts the result of formatting a posting's date with a date format string, exactly like those supported by @code{strftime}. For -example: @samp{%[%Y/%m/%d %H:%M:%S]}. +example: @code{%[%Y/%m/%d %H:%M:%S]}. @item S -Insert the pathname of the file from which the transaction's data was read. +Insert the pathname of the file from which the transaction's data was +read. Only sensible in a register report. @item B -Inserts the beginning character position of that transaction within the file. +Inserts the beginning character position of that transaction within the +file. @item b Inserts the beginning line of that transaction within the file. @item E -Inserts the ending character position of that transaction within the file. +Inserts the ending character position of that transaction within the +file. @item e Inserts the ending line of that transaction within the file. -@item D -By default, this is the same as @samp{%[%Y/%m%/d]}. The date format -used can be changed at any time with the @option{-y} flag, however. -Using @samp{%D} gives the user more control over the way dates are -output. +@c @item D +@c By default, this is the same as @code{%[%Y/%m%/d]}. The date format +@c used can be changed at any time with the @code{-y} flag, however. Using +@c @code{%D} gives the user more control over the way dates are output. @item d -This is the same as the @samp{%D} option, unless the transaction has an -effective date, in which case it prints -@samp{[ACTUAL_DATE=EFFECTIVE_DATE]}. +Returns the data according to the default format. If the transaction +has an effective date, it prints @code{[ACTUAL_DATE=EFFECTIVE_DATE]}. @item X -If a posting has been cleared, this inserts @samp{*} followed by a -space; otherwise nothing is inserted. +If a posting has been cleared, this returns a 1, otherwise returns 0. @item Y -This is the same as @samp{%X}, except that it only displays a state +This is the same as @code{%X}, except that it only displays a state character if all of the member postings have the same state. @item C -Inserts the checking number for a transaction, in parentheses, followed by -a space; if none was specified, nothing is inserted. +Inserts the transaction type. This is the value specified between +parenthesis on the first line of the transaction. @item P Inserts the payee related to a posting. -@item a -Inserts the optimal short name for an account. This is normally used -in balance reports. It prints a parent account's name if that name -has not been printed yet, otherwise it just prints the account's name. +@c @item a +@c Inserts the optimal short name for an account. This is normally used in +@c balance reports. It prints a parent account's name if that name has not +@c been printed yet, otherwise it just prints the account's name. @item A Inserts the full name of an account. -@item W -This is the same as @samp{%A}, except that it first displays the -posting's state @emph{if the transaction's posting states are not -all the same}, followed by the full account name. This is offered as -a printing optimization, so that combined with @samp{%Y}, only the -minimum amount of state detail is printed. +@c @item W +@c This is the same as @code{%A}, except that it first displays the +@c posting's state @emph{if the transaction's posting states are not all +@c the same}, followed by the full account name. This is offered as a +@c printing optimization, so that combined with @code{%Y}, only the minimum +@c amount of state detail is printed. -@item o -Inserts the ``optimized'' form of a posting's amount. This is -used by the print report. In some cases, this inserts nothing; in -others, it inserts the posting amount and its cost. It's use is -not recommend unless you are modifying the print report. +@c @item o +@c Inserts the ``optimized'' form of a posting's amount. This is used by +@c the print report. In some cases, this inserts nothing; in others, it +@c inserts the posting amount and its cost. It's use is not recommended +@c unless you are modifying the print report. -@item n -Inserts the note associated with a posting, preceded by two spaces -and a semi-colon, if it exists. Thus, no none becomes an empty -string, while the note @samp{foo} is substituted as @samp{ ; foo}. @item N Inserts the note associated with a posting, if one exists. @item / -The @samp{%/} construct is special. It separates a format string +The @code{%/} construct is special. It separates a format string between what is printed for the first posting of a transaction, and what is printed for all subsequent postings. If not used, the same format string is used for all postings. @end table -@node --balance-format, New formatting codes, Format Expressions, Format Strings +@node --balance-format, Formatting codes, Format Expressions, Format Strings @section --balance-format -As an example of how flexible the --format strings can be, the default balance format looks like this: +As an example of how flexible the @code{--format} strings can be, the default +balance format looks like this (the various functions are described later): @smallexample "%(justify(scrub(display_total), 20, -1, true, color))" @@ -6682,33 +7372,37 @@ As an example of how flexible the --format strings can be, the default balance f "--------------------\n" @end smallexample -@node New formatting codes, Date and Time Format Codes, --balance-format, Format Strings -@section New Formatting Codes +@node Formatting codes, , --balance-format, Format Strings +@section Formatting Functions and Codes @menu * Field Widths:: * Colors:: * Quantities and Calculations:: * Dates:: +* Date and Time Format Codes:: * Text Formatting:: -* Misc:: +* Data File Parsing Information:: @end menu -@node Field Widths, Colors, New formatting codes, New formatting codes +@node Field Widths, Colors, Formatting codes, Formatting codes @subsection Field Widths -@multitable @columnfractions .3 .2 .5 -@item @strong{Function} @tab @strong{Abbrev.} @tab @strong{Description} +The following codes return the width allocated for the specific fields. +The defaults can be changed using the corresponding command line +options: +@itemize @item @code{date_width} @item @code{payee_width} @item @code{account_width} @item @code{amount_width} @item @code{total_width} -@end multitable +@end itemize -@node Colors, Quantities and Calculations, Field Widths, New formatting codes +@node Colors, Quantities and Calculations, Field Widths, Formatting codes @subsection Colors -The character based formatting ledger can do is limited to the ANSI terminal character colors and font highlight in a normal TTY session. +The character based formatting ledger can do is limited to the ANSI +terminal character colors and font highlights in a normal TTY session. @multitable @columnfractions .3 .3 .3 @item @code{red} @tab @code{magenta} @tab @code{bold} @item @code{green } @tab @code{cyan} @tab @code{underline} @@ -6718,212 +7412,211 @@ The character based formatting ledger can do is limited to the ANSI terminal cha -@node Quantities and Calculations, Dates, Colors, New formatting codes +@node Quantities and Calculations, Dates, Colors, Formatting codes @subsection Quantities and Calculations -@multitable @columnfractions .3 .2 .5 -@item @strong{Function} @tab @strong{Abbrev.} @tab @strong{Description} -@item @code{amount_expr } @tab @code{} @tab -@item @code{abs} @tab @code{U} @tab -@item @code{commodity } @tab @code{} @tab -@item @code{display_amount } @tab @code{t} @tab -@item @code{display_total } @tab @code{T} @tab -@item @code{floor } @tab @code{} @tab -@item @code{get_at } @tab @code{} @tab -@item @code{is_seq } @tab @code{} @tab -@item @code{market } @tab @code{P} @tab -@item @code{percent } @tab @code{} @tab -@item @code{price } @tab @code{} @tab -@item @code{quantity } @tab @code{} @tab -@item @code{rounded } @tab @code{} @tab -@item @code{truncated } @tab @code{} @tab -@item @code{total_expr } @tab @code{} @tab -@item @code{top_amount } @tab @code{} @tab -@item @code{to_boolean } @tab @code{} @tab -@item @code{to_int } @tab @code{} @tab -@item @code{to_amount } @tab @code{} @tab -@item @code{to_balance } @tab @code{} @tab -@item @code{unrounded } @tab @code{} @tab -@end multitable +@table @code +@item amount_expr +@item abs +@item commodity +@item display_amount +@item display_total +@item floor +@item get_at +@item is_seq +@item market +@item percent +@item price +@item quantity +@item rounded +@item truncated +@item total_expr +@item top_amount +@item to_boolean +@item to_int +@item to_amount +@item to_balance +@item unrounded +@end table -@node Dates, Text Formatting, Quantities and Calculations, New formatting codes -@subsection Dates - -@multitable @columnfractions .3 .2 .5 -@item @strong{Function} @tab @strong{Abbrev.} @tab @strong{Description} -@item @code{date } @tab @code{} @tab -@item @code{format_date } @tab @code{} @tab -@item @code{now } @tab @code{} @tab --> d m -@item @code{today } @tab @code{} @tab -@item @code{to_datetime } @tab @code{} @tab -@item @code{to_date } @tab @code{} @tab -@item @code{value_date } @tab @code{} @tab -@end multitable - -@node Text Formatting, Misc, Dates, New formatting codes -@subsection Text Formatting -@subsubsection Summary -@multitable @columnfractions .6 .4 -@item @strong{Function} @tab @strong{Description} -@item @code{ansify_if(str,color) } @tab Colorize the string -@item @code{justify(str, fwidth, lwidth, right, colorize) } @tab Right or left justify the string. -@item @code{join(str) } @tab Remove line feeds from the input string. Mainly used internally for org-mode output -@item @code{quoted(str) } @tab Returns @code{"<str>"}. -@item @code{strip } @tab @code{Removes additional annotations from values.} -@item @code{scrub } @tab @code{S} -@item @code{should_bold } @tab @code{} -@end multitable -@subsubsection Detailed Descriptions +@node Dates, Date and Time Format Codes, Quantities and Calculations, Formatting codes +@subsection Date Functions +The following functions allow you to manipulate and format dates. @table @code -@item ansify_if(value, color) -Surrounds the string representing value with ANSI codes to give it -@code{color} on an TTY display. Has no effect if directed to a file. -@item justify(value, first_width, latter_width, right_justify, colorize) -Right or left justify the string representing @code{value}. The width -of the field in the first line is given by @code{first_width}. For -subsequent lines the width is given by @code{latterwidth}. If -@code{latter_width=-1}, then @code{first_width} is use for all lines. -If @code{right_justify=true} then the field is right justify within the -width of the field. If it is @code{false}, then the field is left -justified and padded to the full width of the field. If @code{colorize} -is true then ledger will honor color settings. -@item join(str) -Replaces line feeds in str with @code{\n}. -@item quoted(str) -Return str surrounded by double quotes, @code{"<str>"}. -@item strip(value) -Values can have numerous annotations, such as effective dates and lot -prices. @code{strip} removes these annotations. +@item date +Returns the date of the current transaction +@item format_date(date, "FORMAT STRING") +formats the date using the given format string. +@item now +Returns the current date and time. If the @code{--now} option is +defined it will return that value. +@item today +Returns the current date. If the @code{--now} option is +defined it will return that value. +@item to_datetime +convert a string to a date-time value +@item to_date +convert a string to date value +@item value_date @end table -@node Misc, , Text Formatting, New formatting codes -@subsection Miscellaneous -@multitable @columnfractions .3 .2 .5 -@item @strong{Function} @tab @strong{Abbrev.} @tab @strong{Description} -@item @code{amount_expr } @tab @code{} @tab -@item @code{abs } @tab @code{} @tab --> U -@item @code{code} @tab @code{} @tab returns the transaction code, the string between the parenthesis after the date. -@item @code{commodity } @tab @code{} @tab -@item @code{display_amount } @tab @code{} @tab --> t -@item @code{display_total } @tab @code{} @tab --> T -@item @code{date } @tab @code{} @tab -@item @code{format_date } @tab @code{} @tab -@item @code{format } @tab @code{} @tab -@item @code{floor } @tab @code{} @tab -@item @code{get_at } @tab @code{} @tab -@item @code{is_seq } @tab @code{} @tab -@item @code{justify } @tab @code{} @tab -@item @code{join } @tab @code{} @tab -@item @code{market --> P } @tab @code{} @tab -@item @code{null } @tab @code{} @tab -@item @code{now --> d m } @tab @code{} @tab -@item @code{options } @tab @code{} @tab -@item @code{post } @tab @code{} @tab -@item @code{percent } @tab @code{} @tab -@item @code{price } @tab @code{} @tab -@item @code{print } @tab @code{} @tab -@item @code{quoted } @tab @code{} @tab -@item @code{quantity } @tab @code{} @tab -@item @code{rounded } @tab @code{} @tab -@item @code{scrub } @tab @code{} @tab -@item @code{strip --> S } @tab @code{} @tab -@item @code{should_bold } @tab @code{} @tab -@item @code{truncated } @tab @code{} @tab -@item @code{total_expr } @tab @code{} @tab -@item @code{today } @tab @code{} @tab -@item @code{top_amount } @tab @code{} @tab -@item @code{to_boolean } @tab @code{} @tab -@item @code{to_int } @tab @code{} @tab -@item @code{to_datetime } @tab @code{} @tab -@item @code{to_date } @tab @code{} @tab -@item @code{to_amount } @tab @code{} @tab -@item @code{to_balance } @tab @code{} @tab -@item @code{to_spring } @tab @code{} @tab -@item @code{to_mask } @tab @code{} @tab -@item @code{to_sequence } @tab @code{} @tab -@item @code{unrounded } @tab @code{} @tab -@item @code{value_date } @tab @code{} @tab -@end multitable -@node Date and Time Format Codes, , New formatting codes, Format Strings -@section Date and Time Format Codes +@menu +* Date and Time Format Codes:: +@end menu + +@node Date and Time Format Codes, Text Formatting, Dates, Formatting codes +@subsection Date and Time Format Codes Date and time format are specified as strings of single letter codes preceded by percent signs. Any separator, or no separator can be specified. -@subsection Dates +@subsubsection Days Dates are formed from a combination of day, month and year codes, in whatever order you prefer: -@option{%Y} is keyword for four digit year +@table @code +@item %Y +Four digit year -@option{%y} is keyword for two digit year +@item %y +Two digit year -@option{%m} is keyword for two digit month +@item %m +Two digit month -@option{%d} is keyword for two digit date +@item %d +Two digit date +@end table -@noindent So @code{"%Y%m%d"} yields @code{20111214} which provides a date that is simple to sort. +@noindent So @code{"%Y%m%d"} yields @code{20111214} which provides a date that is simple to sort on. -@subsection Weekdays +@subsubsection Weekdays You can have additional weekday information in your date with @code{%A} as -@option{%m-%d-%Y %A} yields @code{02-10-2010 Wednesday} - -@option{%A %m-%d-%Y} yields @code{Wednesday 02-10-2010} +@table @code +@item %m-%d-%Y %A +yields @code{02-10-2010 Wednesday} +@item %A %m-%d-%Y +yields @code{Wednesday 02-10-2010} +@end table @noindent These are options you can select for weekday -@option{%a} weekday, abbreviated Wed - -@option{%A} weekday, full Wednesday - -@option{%d} day of the month (dd), zero padded 10 - -@option{%e} day of the month (dd) 10 - -@option{%j} day of year, zero padded 000-366 - -@option{%u} day of week starting with Monday (1), i.e. @code{mtwtfss} 3 - -@option{%w} day of week starting with Sunday (0), i.e. @code{smtwtfs} 3 - -@subsection Month +@table @code +@item %a +weekday, abbreviated Wed +@item %A +weekday, full Wednesday +@item %d +day of the month (dd), zero padded 10 +@item %e +day of the month (dd) 10 +@item %j +day of year, zero padded 000-366 +@item %u +day of week starting with Monday (1), i.e. @code{mtwtfss} 3 +@item %w +day of week starting with Sunday (0), i.e. @code{smtwtfs} 3 +@end table +@subsubsection Month You can have additional month information in your date with @code{%B} as +@table @code +@item %m-%d-%Y %B +yields @code{ 02-10-2010 Februrary} -@option{%m-%d-%Y %B} yields @code{ 02-10-2010 Februrary} - -@option{%B %m-%d-%Y} yields @code{February 02-10-2010} - +@item %B %m-%d-%Y +yields @code{February 02-10-2010} +@end table @noindent These are options you can select for month +@table @code +@item %m +@code{mm} month as two digits -@option{%m} @code{mm} month as two digits +@item %b +Locale’s abbreviated month, for example @code{02} might be abbreviated as @code{Feb} -@option{%b} @code{Mon}, locale’s abbreviated Feb +@item %B +Locale’s full month, variable length February +@end table -@option{%B} locale’s full month, variable length February +@subsubsection Miscellaneous Date Codes +Additional date format parameters which can be used : +@table @code +@item %U +week number Sunday as first day of week 01–53 +@item %W +week number Monday as first day of week 01–53 +@item %V +week of the year 01–53 +@item %C +@code{cc} century 00–99 +@item %D +yields @code{mm/dd/yy 02/10/10} +@item %x +locale’s date representation @code{02/10/2010} for the U.S. +@item %F +yields @code{%Y-%m-%d 2010-02-10} +@end table +@menu +* Text Formatting:: +* Data File Parsing Information:: +* Misc:: +@end menu -@subsection Miscellaneous Date Codes -Additional date format parameters which can be used : +@node Text Formatting, Data File Parsing Information, Date and Time Format Codes, Formatting codes +@subsection Text Formatting +The following format functions allow you limited formatting of text: +@table @code +@item ansify_if(value, color) +Surrounds the string representing value with ANSI codes to give it +@code{color} on an TTY display. Has no effect if directed to a file. +@item justify(value, first_width, latter_width, right_justify, colorize) +Right or left justify the string representing @code{value}. The width +of the field in the first line is given by @code{first_width}. For +subsequent lines the width is given by @code{latterwidth}. If +@code{latter_width=-1}, then @code{first_width} is use for all lines. +If @code{right_justify=true} then the field is right justify within the +width of the field. If it is @code{false}, then the field is left +justified and padded to the full width of the field. If @code{colorize} +is true then ledger will honor color settings. +@item join(STR) +Replaces line feeds in @code{STR} with @code{\n}. +@item quoted(STR) +Return @code{STR} surrounded by double quotes, @code{"STR"}. +@item strip(value) +Values can have numerous annotations, such as effective dates and lot +prices. @code{strip} removes these annotations. +@end table -@option{%U} week number Sunday as first day of week 01–53 +@node Data File Parsing Information, , Text Formatting, Formatting codes +@subsection Data File Parsing Information -@option{%W} week number Monday as first day of week 01–53 +The following format strings provide locational metadata +regarding the coordinates of entries in the source data file(s) that +generated the posting. -@option{%V} week of the year 01–53 +@table @code +@item filename +name of ledger data file from whence posting came, abbreviated @code{S} +@item beg_pos +character position in @code{filename} where entry for posting begins, abbreviated @code{B} +@item end_pos +character position in @code{filename} where entry for posting ends, abbreviated @code{E} +@item beg_line +line number in @code{filename} where entry for posting begins, abbreviated @code{b} +@item end_line +line number in @code{filename} where posting's entry for posting ends, abbreviated @code{e} +@end table -@option{%C} @code{cc} century 00–99 -@option{%D} yields @code{mm/dd/yy 02/10/10} -@option{%x} locale’s date representation @code{02/10/2010} for the U.S. -@option{%F} yields @code{%Y-%m-%d 2010-02-10} @node Ledger for Developers, Extending with Python, Format Strings, Top @@ -6984,7 +7677,7 @@ Those tiers are: strings, etc. If you try to apply an operation between two values that makes no sense (like dividing an amount by a balance), an error occurs at runtime, rather than at compile-time (as would happen if you actually tried - to divide an amount_t by a balance_t). + to divide an @code{amount_t} by a @code{balance_t}). This is the core data type for the value expression language. @@ -7003,9 +7696,9 @@ Those tiers are: @item Query expressions Expressions can be onerous to type at the command-line, so there's a - shorthand for reporting called "query expressions". These add no + shorthand for reporting called ``query expressions''. These add no functionality of there own, but are purely translated from the input string - (cash) down to the corresponding value expression (account =~ /cash/). + (cash) down to the corresponding value expression @code{(account =~ /cash/)}. This is a convenience layer. @item Format strings @@ -7013,7 +7706,7 @@ Those tiers are: Format strings let you interpolate value expressions into string, with the requirement that any interpolated value have a string representation. Really all this does is calculate the value expression in the current - report context, call the resulting value's "to_string()" method, and stuffs + report context, call the resulting value's @code{to_string()} method, and stuffs the result into the output string. It also provides printf-like behavior, such as min/max width, right/left justification, etc. @@ -7046,19 +7739,19 @@ Those tiers are: @item The Journal object Finally, all transactions with their postings, and all accounts, are owned - by a journal_t object. This is the go-to object for querying ad reporting + by a @code{journal_t} object. This is the go-to object for querying ad reporting on your data. @item Textual journal parser There is a textual parser, wholly contained in textual.cc, which knows how - to parse text into journal objects, which then get "finalized" and added to + to parse text into journal objects, which then get ``finalized'' and added to the journal. Finalization is the step that enforces the double-entry guarantee. @item Iterators - Every journal object is "iterable", and these iterators are defined in + Every journal object is ``iterable'', and these iterators are defined in iterators.h and iterators.cc. This iteration logic is kept out of the basic journal objects themselves for the sake of modularity. @@ -7066,12 +7759,12 @@ Those tiers are: Another abstraction isolated to its own layer, this class encapsulating the comparison of journal objects, based on whatever value expression the user - passed to --sort. + passed to @code{--sort}. @item Temporaries Many reports bring pseudo-journal objects into existence, like postings - which report totals in a "<Total>" account. These objects are created and + which report totals in a @code{<Total>} account. These objects are created and managed by a temporaries_t object, which gets used in many places by the reporting filters. @@ -7103,7 +7796,7 @@ Those tiers are: iterator depends on the type of report. This iterator is then walked, and every object yielded from the iterator is - passed to an "item handler", whose type is directly related to the type of + passed to an ``item handler'', whose type is directly related to the type of the iterator. There are many, many item handlers, which can be chained together. Each @@ -7112,22 +7805,22 @@ Those tiers are: filters which compute the running totals; that queue and sort all the input items before playing them back out in a new order; that filter out items which fail to match a predicate, etc. Almost every reporting feature in - Ledger is related to one or more filters. Looking at filters.h, I see over + Ledger is related to one or more filters. Looking at @code{filters.h}, I see over 25 of them defined currently. @item The filter chain How filters get wired up, and in what order, is a complex process based on all the various options specified by the user. This is the job of the - chain logic, found entirely in chain.cc. It took a really long time to get + chain logic, found entirely in @code{chain.cc}. It took a really long time to get this logic exactly write, which is why I haven't exposed this layer to the Python bridge yet. @item Output modules Although filters are great and all, in the end you want to see stuff. This - is the job of special "leaf" filters call output modules. They are - implemented just like a regular filter, but they don't have a "next" filter + is the job of special ``leaf'' filters call output modules. They are + implemented just like a regular filter, but they don't have a ``next'' filter to pass the time on down to. Instead, they are the end of the line and must do something with the item that results in the user seeing something on their screen or in a file. @@ -7151,7 +7844,7 @@ Those tiers are: This creates the global scope object, performs error reporting, and handles command-line options which must precede even the creation of the global - scope, such as --debug. + scope, such as @code{--debug}. @end itemize And that's Ledger in a nutshell. All the rest are details, such as which @@ -7187,12 +7880,12 @@ useful to follow standard accounting practice (@pxref{Principles of Accounting}). Since an amount is missing from the second posting, it is assumed to -be the inverse of the first. This guarantee the cardinal rule of +be the inverse of the first. This guarantees the cardinal rule of double-entry accounting: the sum of every transaction must balance to zero, or it is in error. Whenever Ledger encounters a @dfn{null posting} in a transaction, it uses it to balance the remainder. -It is also typical---though not enforced---to think of the first +It is also typical, though not enforced, to think of the first posting as the destination, and the final as the source. Thus, the amount of the first posting is typically positive. Consider: @@ -7216,7 +7909,7 @@ amount of the first posting is typically positive. Consider: @node Comments and meta-data, Specifying Amounts, Journal File Format, Journal File Format @subsection Comments and meta-data -Comments are generally started using a ';'. However, in order to +Comments are generally started using a @code{;}. However, in order to increase compatibility with other text manipulation programs and methods three additional comment characters are valid if used at the beginning of a line: @code{#}, @code{|}, and @code{*}. @@ -7259,13 +7952,13 @@ now, a word must be said about how Ledger stores numbers. Every number parsed by Ledger is stored internally as an infinite-precision rational value. Floating-point math is never used, as it cannot be trusted to maintain precision of values. So, in the -case of @samp{1000.00} above, the internal value is @samp{100000/100}. +case of @code{1000.00} above, the internal value is @code{100000/100}. While rational numbers are great at not losing precision, the question -arises: How should they be displayed? A number like @samp{100000/100} +arises: How should they be displayed? A number like @code{100000/100} is no problem, since it represents a clean decimal fraction. But what -about when the number @samp{1/1} is divided by three? How should one -print @samp{1/3}, an infinitely repeating decimal? +about when the number @code{1/1} is divided by three? How should one +print @code{1/3}, an infinitely repeating decimal? Ledger gets around this problem by rendering rationals into decimal at the last possible moment, and only for display. As such, some @@ -7276,11 +7969,11 @@ rarely, but even then it does not reflect adjustment of the @emph{internal amount}, only the displayed amount. What has still not been answered is how Ledger rounds values. Should -@samp{1/3} be printed as @samp{0.33} or @samp{0.33333}? For +@code{1/3} be printed as @code{0.33} or @code{0.33333}? For commoditized amounts, the number of decimal places is decided by observing how each commodity is used; but in the case of integer amounts, an arbitrary factor must be chosen. Initially, this factor -is six. Thus, @samp{1/3} is printed back as @samp{0.333333}. +is six. Thus, @code{1/3} is printed back as @code{0.333333}. Further, this rounding factor becomes associated with each particular value, and is carried through mathematical operations. For example, if that particular number were multiplied by itself, the decimal @@ -7303,10 +7996,10 @@ characters are allowed in a commodity name, except for the following: @itemize @bullet @item Any kind of white-space @item Numerical digits -@item Punctuation: @samp{.,;:?!} -@item Mathematical and logical operators: @samp{-+*/^&|=} -@item Bracketing characters: @samp{<>[]()}@{@} -@item The at symbol: @samp{@@} +@item Punctuation: @code{.,;:?!} +@item Mathematical and logical operators: @code{-+*/^&|=} +@item Bracketing characters: @code{<>[]()}@{@} +@item The at symbol: @code{@@} @end itemize And yet, any of these may appear in a commodity name if it is @@ -7322,9 +8015,9 @@ part is the commodity. Another feature of commoditized amounts is that they are reported back in the same form as parsed. If you specify dollar amounts using -@samp{$100}, they will print the same; likewise with @samp{100 $} or -@samp{$100.000}. You may even use decimal commas, such as -@samp{$100,00}, or thousand-marks, as in @samp{$10,000.00}. +@code{$100}, they will print the same; likewise with @code{100 $} or +@code{$100.000}. You may even use decimal commas, such as +@code{$100,00}, or thousand-marks, as in @code{$10,000.00}. These display characteristics become associated with the commodity, with the result being that all amounts of the same commodity are reported @@ -7388,7 +8081,7 @@ postings are involved: Assets:Checking @end smallexample -Here the implied cost is @samp{$57.00}, which is entered into the null +Here the implied cost is @code{$57.00}, which is entered into the null posting automatically so that the transaction balances. @node Primary commodities, , Posting costs, Journal File Format @@ -7416,7 +8109,7 @@ on the placement of the commodity in the transaction: @end smallexample The only case where knowledge of primary versus secondary comes into -play is in reports that use the @option{-V} or @option{-B} options. +play is in reports that use the @code{-V} or @code{-B} options. With these, only primary commodities are shown. If a transaction uses only one commodity, this commodity is also @@ -7447,7 +8140,7 @@ interface functions. The Session is where objects live like the Commodity's that Amount's refer to. The make a Session useful, you must read a Journal into it, using the function -`@samp{read_journal}`. This reads Ledger data from the given file, populates a +`@code{read_journal}`. This reads Ledger data from the given file, populates a Journal object within the current Session, and returns a reference to that Journal object. @@ -7583,11 +8276,16 @@ need to create sums of multiple commodities, use a Balance. For example: @node Major Changes from version 2.6, Example Data File, Extending with Python, Top @chapter Major Changes from version 2.6 +@itemize +@item OFX support has been removed from Ledger 3.0 +@item single character value expressions are deprecated and should be changed to the new value expressions available in 3.0 +@end itemize + @node Example Data File, Miscellaneous Notes, Major Changes from version 2.6, Top @appendix Example Journal File: drewr.dat The following journal file is included with the source distribution of ledger. It is called @file{drewr.dat} and exhibits many ledger - features, include automatic and virtual transactions, + features, include automatic and virtual transactions, @smallexample ; -*- ledger -*- @@ -7690,6 +8388,7 @@ ledger register Checking --sort d -d 'd>[2011/04/01]' until 2011/05/25 (Liabilities:Tithe Owed) -1.0 @end smallexample + @node Concept Index, Command Index, Miscellaneous Notes, Top @unnumbered Concept Index diff --git a/lib/Makefile b/lib/Makefile index 92ec38ce..68755f18 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -2,7 +2,7 @@ # This is only important if you intend to produce a Ledger binary for # installation. -STOW_ROOT = /usr/local/stow +STOW_ROOT = /usr/local/Cellar/boost PRODUCTS = $(HOME)/Products GCC_VERSION = 4.7 @@ -37,14 +37,10 @@ endif BOOST_FLAGS = toolset=$(BOOST_TOOLSET) --layout=versioned \ link=shared threading=single $(BOOST_DEFINES) BOOST_DIR = boost_$(BOOST_VERSION)-$(DIR_SUFFIX) -BOOST_STOW = $(STOW_ROOT)/$(BOOST_DIR) +BOOST_STOW = $(STOW_ROOT)/$(BOOST_VERSION) BOOST_BUILD = $(PRODUCTS)/$(BOOST_DIR) -ICU_FLAGS = -sHAVE_ICU=1 -sICU_PATH=$(STOW_ROOT)/icu-$(DIR_SUFFIX) -BOOST_ICU_DIR = boost_$(BOOST_VERSION)-icu-$(DIR_SUFFIX) -BOOST_ICU_STOW = $(STOW_ROOT)/$(BOOST_ICU_DIR) -BOOST_ICU_BUILD = $(PRODUCTS)/$(BOOST_ICU_DIR) -all: boost-build #icu-build boost-icu-build +all: boost-build prepare-boost: perl -i -pe 's/local command = \[ common\.get-invocation-command darwin : g\+\+ : .*/local command = [ common.get-invocation-command darwin : g++ : $(CXX) ] ;/;' $(BOOST_SOURCE)/tools/build/v2/tools/darwin.jam @@ -56,29 +52,5 @@ boost-build: prepare-boost ./b2 $(OPTJ) debug release --prefix=$(BOOST_STOW) \ --build-dir=$(BOOST_BUILD) $(BOOST_FLAGS) install) -icu-build: - -(cd icu/source; make distclean) - (cd icu/source; sh autogen.sh; \ - ./configure CPPFLAGS="$(CPPFLAGS)" \ - CFLAGS="$(CFLAGS)" \ - LDFLAGS="$(LDFLAGS)" \ - CC="$(CC)" CXX="$(CXX)" LD="$(LD)" \ - --enable-static --enable-debug \ - --prefix=$(STOW_ROOT)/icu-$(DIR_SUFFIX) && \ - make install) - -boost-icu-build: - (cd $(BOOST_SOURCE) && \ - sh bootstrap.sh && \ - ./bjam $(OPTJ) debug --prefix=$(BOOST_ICU_STOW) \ - --build-dir=$(BOOST_ICU_BUILD) \ - $(BOOST_FLAGS) $(ICU_FLAGS) install) - clean: -rm -fr $(BOOST_STOW) $(BOOST_BUILD) - -rm -fr $(BOOST_ICU_STOW) $(BOOST_ICU_BUILD) - -rm -fr $(STOW_ROOT)/icu-$(DIR_SUFFIX) - -(cd icu/source; make distclean) - -lib-clean: - -(cd icu/source; make distclean) diff --git a/lib/build.sh b/lib/build.sh index 4fadccfa..4f3f2e7f 100755 --- a/lib/build.sh +++ b/lib/build.sh @@ -4,7 +4,7 @@ # clang-3.1 from MacPorts. I build my own Boost instead of using MacPorts' # Boost in order to get better debugging support, and to link with libc++. -export PATH=$PATH:/opt/local/lib/openmpi/bin +#export PATH=$PATH:/opt/local/lib/openmpi/bin cat > ~/user-config.jam <<EOF using clang-darwin : : "/usr/local/bin/clang++" : <cxxflags>-std=c++11 ; @@ -14,4 +14,4 @@ EOF # instead of /usr/local/lib/libc++.1.dylib make CXX=clang++ LD=clang++ CC=clang OPTJ=-j20 \ BOOST_TOOLSET=clang-darwin DIR_SUFFIX=clang31 \ - BOOST_DEFINES="-sHAVE_ICONV=1 -sICONV_PATH=/usr/local -sHAVE_ICU=1 -sICU_PATH=/usr/local cxxflags=\"-g -std=c++11 $* -nostdlibinc -isystem /usr/local/include -isystem /opt/local/include -isystem /usr/local/include/c++/v1 -isystem /usr/include -stdlib=libc++\" linkflags=\"-g $* -L/usr/local/lib -L/opt/local/lib -L/usr/lib /usr/local/lib/libc++.dylib -stdlib=libc++\"" + BOOST_DEFINES="-sHAVE_ICONV=1 -sHAVE_ICU=1 -sICU_PATH=/usr/local/opt/icu4c cxxflags=\"-g -std=c++11 $* -nostdlibinc -isystem /usr/local/include -isystem /usr/local/include/c++/v1 -isystem /usr/include -stdlib=libc++\" linkflags=\"-g $* -L/usr/local/lib -L/usr/lib /usr/local/lib/libc++.dylib -stdlib=libc++\"" diff --git a/lisp/ldg-mode.el b/lisp/ldg-mode.el index 6090a312..e36dc969 100644 --- a/lisp/ldg-mode.el +++ b/lisp/ldg-mode.el @@ -1,3 +1,16 @@ +(defsubst ledger-current-year () + (format-time-string "%Y")) +(defsubst ledger-current-month () + (format-time-string "%m")) + +(defvar ledger-year (ledger-current-year) + "Start a ledger session with the current year, but make it +customizable to ease retro-entry.") +(defvar ledger-month (ledger-current-month) + "Start a ledger session with the current month, but make it +customizable to ease retro-entry.") + + (defcustom ledger-default-acct-transaction-indent " " "Default indentation for account transactions in an entry." :type 'string @@ -19,6 +32,7 @@ (defvar ledger-mode-abbrev-table) + ;;;###autoload (define-derived-mode ledger-mode text-mode "Ledger" "A mode for editing ledger data files." @@ -51,8 +65,41 @@ (define-key map [tab] 'pcomplete) (define-key map [(control ?i)] 'pcomplete) (define-key map [(control ?c) tab] 'ledger-fully-complete-entry) - (define-key map [(control ?c) (control ?i)] 'ledger-fully-complete-entry)) + (define-key map [(control ?c) (control ?i)] 'ledger-fully-complete-entry) + (define-key map [(control ?c) (control ?o) (control ?r)] 'ledger-report) + (define-key map [(control ?c) (control ?o) (control ?g)] 'ledger-report-goto) + (define-key map [(control ?c) (control ?o) (control ?a)] 'ledger-report-redo) + (define-key map [(control ?c) (control ?o) (control ?s)] 'ledger-report-save) + (define-key map [(control ?c) (control ?o) (control ?e)] 'ledger-report-edit) + (define-key map [(control ?c) (control ?o) (control ?k)] 'ledger-report-kill) + + + (define-key map [menu-bar] (make-sparse-keymap "ldg-menu")) + (define-key map [menu-bar ldg-menu] (cons "Ledger" map)) + (define-key map [menu-bar ldg-menu lrk] '("Kill Report" . ledger-report-kill)) + (define-key map [menu-bar ldg-menu lre] '("Edit Report" . ledger-report-edit)) + (define-key map [menu-bar ldg-menu lrs] '("Save Report" . ledger-report-save)) + (define-key map [menu-bar ldg-menu lrr] '("Re-run Report" . ledger-report-redo)) + (define-key map [menu-bar ldg-menu lrg] '("Goto Report" . ledger-report-goto)) + (define-key map [menu-bar ldg-menu lr] '("Run Report" . ledger-report)) + (define-key map [menu-bar ldg-menu s5] '("--")) + (define-key map [menu-bar ldg-menu sm] '("Set Month" . ledger-set-month)) + (define-key map [menu-bar ldg-menu sy] '("Set Year" . ledger-set-year)) + (define-key map [menu-bar ldg-menu s1] '("--")) + (define-key map [menu-bar ldg-menu so] '("Sort Buffer" . ledger-sort)) + (define-key map [menu-bar ldg-menu s2] '("--")) + (define-key map [menu-bar ldg-menu te] '("Toggle Current Posting" . ledger-toggle-current)) + (define-key map [menu-bar ldg-menu tt] '("Toggle Current Transaction" . ledger-toggle-current-entry)) + (define-key map [menu-bar ldg-menu s4] '("--")) + (define-key map [menu-bar ldg-menu de] '("Delete Entry" . ledger-delete-current-entry)) + (define-key map [menu-bar ldg-menu ae] '("Add Entry" . ledger-add-entry)) + (define-key map [menu-bar ldg-menu s3] '("--")) + (define-key map [menu-bar ldg-menu re] '("Reconcile Account" . ledger-reconcile))) + + + + (ledger-report-patch-reports (current-buffer))) (defun ledger-time-less-p (t1 t2) @@ -76,8 +123,50 @@ Return the difference in the format of a time value." (if (ledger-time-less-p moment date) (throw 'found t))))))) +(defun ledger-iterate-entries (callback) + (goto-char (point-min)) + (let* ((now (current-time)) + (current-year (nth 5 (decode-time now)))) + (while (not (eobp)) + (when (looking-at + (concat "\\(Y\\s-+\\([0-9]+\\)\\|" + "\\([0-9]\\{4\\}+\\)?[./]?" + "\\([0-9]+\\)[./]\\([0-9]+\\)\\s-+" + "\\(\\*\\s-+\\)?\\(.+\\)\\)")) + (let ((found (match-string 2))) + (if found + (setq current-year (string-to-number found)) + (let ((start (match-beginning 0)) + (year (match-string 3)) + (month (string-to-number (match-string 4))) + (day (string-to-number (match-string 5))) + (mark (match-string 6)) + (desc (match-string 7))) + (if (and year (> (length year) 0)) + (setq year (string-to-number year))) + (funcall callback start + (encode-time 0 0 0 day month + (or year current-year)) + mark desc))))) + (forward-line)))) + +(defun ledger-set-year (newyear) + "Set ledger's idea of the current year to the prefix argument." + (interactive "p") + (if (= newyear 1) + (setq ledger-year (read-string "Year: " (ledger-current-year))) + (setq ledger-year (number-to-string newyear)))) + +(defun ledger-set-month (newmonth) + "Set ledger's idea of the current month to the prefix argument." + (interactive "p") + (if (= newmonth 1) + (setq ledger-month (read-string "Month: " (ledger-current-month))) + (setq ledger-month (format "%02d" newmonth)))) + (defun ledger-add-entry (entry-text &optional insert-at-point) - (interactive "sEntry: ") + (interactive (list + (read-string "Entry: " (concat ledger-year "/" ledger-month "/")))) (let* ((args (with-temp-buffer (insert entry-text) (eshell-parse-arguments (point-min) (point-max)))) @@ -95,7 +184,7 @@ Return the difference in the format of a time value." (insert (with-temp-buffer (setq exit-code - (apply #'ledger-run-ledger ledger-buf "entry" + (apply #'ledger-exec-ledger ledger-buf ledger-buf "entry" (mapcar 'eval args))) (goto-char (point-min)) (if (looking-at "Error: ") diff --git a/lisp/ldg-new.el b/lisp/ldg-new.el index 8505fe4a..d9e0fc60 100644 --- a/lisp/ldg-new.el +++ b/lisp/ldg-new.el @@ -32,12 +32,17 @@ ;;; Commentary: -(require 'ldg-post) -(require 'ldg-mode) (require 'ldg-complete) -(require 'ldg-state) +(require 'ldg-exec) +(require 'ldg-mode) +(require 'ldg-post) +(require 'ldg-reconcile) +(require 'ldg-register) (require 'ldg-report) - +(require 'ldg-state) +(require 'ldg-test) +(require 'ldg-texi) +(require 'ldg-xact) ;(autoload #'ledger-mode "ldg-mode" nil t) ;(autoload #'ledger-fully-complete-entry "ldg-complete" nil t) ;(autoload #'ledger-toggle-current "ldg-state" nil t) diff --git a/lisp/ldg-reconcile.el b/lisp/ldg-reconcile.el index baeadc33..08dbc587 100644 --- a/lisp/ldg-reconcile.el +++ b/lisp/ldg-reconcile.el @@ -4,18 +4,24 @@ (defvar ledger-acct nil) (defun ledger-display-balance () + "Calculate the cleared balance of the account being reconciled" (let ((buffer ledger-buf) (account ledger-acct)) (with-temp-buffer - (let ((exit-code (ledger-run-ledger buffer "-C" "balance" account))) - (if (/= 0 exit-code) - (message "Error determining cleared balance") - (goto-char (1- (point-max))) - (goto-char (line-beginning-position)) - (delete-horizontal-space) - (message "Cleared balance = %s" - (buffer-substring-no-properties (point) - (line-end-position)))))))) + (ledger-exec-ledger buffer (current-buffer) "-C" "balance" account) + (goto-char (1- (point-max))) + (goto-char (line-beginning-position)) + (delete-horizontal-space) + (message "Cleared balance = %s" + (buffer-substring-no-properties (point) + (line-end-position)))))) + +(defun is-stdin (file) + "True if ledger file is standard input" + (or + (equal file "") + (equal file "<stdin>") + (equal file "/dev/stdin"))) (defun ledger-reconcile-toggle () (interactive) @@ -23,18 +29,19 @@ (account ledger-acct) (inhibit-read-only t) cleared) - (when (or (equal (car where) "<stdin>") (equal (car where) "/dev/stdin")) + (when (is-stdin (car where)) (with-current-buffer ledger-buf - (goto-char (cdr where)) - (setq cleared (ledger-toggle-current 'pending))) + (goto-char (cdr where)) + (setq cleared (ledger-toggle-current-entry))) (if cleared - (add-text-properties (line-beginning-position) - (line-end-position) - (list 'face 'bold)) - (remove-text-properties (line-beginning-position) - (line-end-position) - (list 'face)))) - (forward-line))) + (add-text-properties (line-beginning-position) + (line-end-position) + (list 'face 'bold)) + (remove-text-properties (line-beginning-position) + (line-end-position) + (list 'face)))) + (forward-line) + (ledger-display-balance))) (defun ledger-reconcile-refresh () (interactive) @@ -62,7 +69,7 @@ (defun ledger-reconcile-delete () (interactive) (let ((where (get-text-property (point) 'where))) - (when (or (equal (car where) "<stdin>") (equal (car where) "/dev/stdin")) + (when (is-stdin (car where)) (with-current-buffer ledger-buf (goto-char (cdr where)) (ledger-delete-current-entry)) @@ -74,7 +81,7 @@ (defun ledger-reconcile-visit () (interactive) (let ((where (get-text-property (point) 'where))) - (when (or (equal (car where) "<stdin>") (equal (car where) "/dev/stdin")) + (when (is-stdin (car where)) (switch-to-buffer-other-window ledger-buf) (goto-char (cdr where))))) @@ -97,7 +104,7 @@ (let ((where (get-text-property (point) 'where)) (face (get-text-property (point) 'face))) (if (and (eq face 'bold) - (or (equal (car where) "<stdin>") (equal (car where) "/dev/stdin"))) + (when (is-stdin (car where)))) (with-current-buffer ledger-buf (goto-char (cdr where)) (ledger-toggle-current 'cleared)))) @@ -105,7 +112,48 @@ (ledger-reconcile-save)) (defun ledger-do-reconcile () - ) + "get the uncleared transactions in the account and display them in the *Reconcile* buffer" + (let* ((buf ledger-buf) + (account ledger-acct) + (items + (with-temp-buffer + (ledger-exec-ledger buf (current-buffer) "--uncleared" "--real" + "emacs" account) + (goto-char (point-min)) + (unless (eobp) + (unless (looking-at "(") + (error (buffer-string))) + (read (current-buffer)))))) + (dolist (item items) + (let ((index 1)) + (dolist (xact (nthcdr 5 item)) + (let ((beg (point)) + (where + (with-current-buffer buf + (cons + (nth 0 item) + (if ledger-clear-whole-entries + (save-excursion + (goto-line (nth 1 item)) + (point-marker)) + (save-excursion + (goto-line (nth 0 xact)) + (point-marker))))))) + (insert (format "%s %-4s %-30s %-30s %15s\n" + (format-time-string "%Y/%m/%d" (nth 2 item)) + (nth 3 item) + (nth 4 item) (nth 1 xact) (nth 2 xact))) + (if (nth 3 xact) + (set-text-properties beg (1- (point)) + (list 'face 'bold + 'where where)) + (set-text-properties beg (1- (point)) + (list 'where where)))) + (setq index (1+ index))))) + (goto-char (point-min)) + (set-buffer-modified-p nil) + (toggle-read-only t))) + (defun ledger-reconcile (account) (interactive "sAccount to reconcile: ") @@ -138,4 +186,22 @@ (define-key map [?p] 'previous-line) (define-key map [?s] 'ledger-reconcile-save) (define-key map [?q] 'ledger-reconcile-quit) + + (define-key map [menu-bar] (make-sparse-keymap "ldg-recon-menu")) + (define-key map [menu-bar ldg-recon-menu] (cons "Reconcile" map)) + (define-key map [menu-bar ldg-recon-menu qui] '("Quit" . ledger-reconcile-quit)) + (define-key map [menu-bar ldg-recon-menu sep1] '("--")) + (define-key map [menu-bar ldg-recon-menu pre] '("Previous Entry" . previous-line)) + (define-key map [menu-bar ldg-recon-menu vis] '("Visit Entry" . ledger-reconcile-visit)) + (define-key map [menu-bar ldg-recon-menu nex] '("Next Entry" . next-line)) + (define-key map [menu-bar ldg-recon-menu sep2] '("--")) + (define-key map [menu-bar ldg-recon-menu del] '("Delete Entry" . ledger-reconcile-delete)) + (define-key map [menu-bar ldg-recon-menu add] '("Add Entry" . ledger-reconcile-add)) + (define-key map [menu-bar ldg-recon-menu tog] '("Toggle Entry" . ledger-reconcile-toggle)) + (define-key map [menu-bar ldg-recon-menu sep3] '("--")) + (define-key map [menu-bar ldg-recon-menu ref] '("Refresh" . ledger-reconcile-refresh)) + (define-key map [menu-bar ldg-recon-menu sav] '("Save" . ledger-reconcile-save)) + (use-local-map map))) + +(provide 'ldg-reconcile)
\ No newline at end of file diff --git a/lisp/ldg-report.el b/lisp/ldg-report.el index 9a964195..a1ffe3b0 100644 --- a/lisp/ldg-report.el +++ b/lisp/ldg-report.el @@ -70,6 +70,21 @@ text that should replace the format specifier." (define-key map [(control ?c) (control ?l) (control ?e)] 'ledger-report-edit) (define-key map [(control ?c) (control ?c)] 'ledger-report-visit-source) + + + (define-key map [menu-bar] (make-sparse-keymap "ldg-rep")) + (define-key map [menu-bar ldg-rep] (cons "Reports" map)) + + (define-key map [menu-bar ldg-rep lrq] '("Quit" . ledger-report-quit)) + (define-key map [menu-bar ldg-rep s2] '("--")) + (define-key map [menu-bar ldg-rep lrd] '("Scroll Down" . scroll-down)) + (define-key map [menu-bar ldg-rep lru] '("Scroll Up" . scroll-up)) + (define-key map [menu-bar ldg-rep s1] '("--")) + (define-key map [menu-bar ldg-rep lrk] '("Kill Report" . ledger-report-kill)) + (define-key map [menu-bar ldg-rep lrr] '("Re-run Report" . ledger-report-redo)) + (define-key map [menu-bar ldg-rep lre] '("Edit Report" . ledger-report-edit)) + (define-key map [menu-bar ldg-rep lrs] '("Save Report" . ledger-report-save)) + (use-local-map map))) (defun ledger-report-read-name () diff --git a/lisp/ldg-xact.el b/lisp/ldg-xact.el index e1f165a7..6e14a14c 100644 --- a/lisp/ldg-xact.el +++ b/lisp/ldg-xact.el @@ -18,3 +18,4 @@ (lambda () (forward-paragraph)))))) +(provide 'ldg-xact)
\ No newline at end of file diff --git a/src/account.h b/src/account.h index a2fcb8de..3642ada0 100644 --- a/src/account.h +++ b/src/account.h @@ -308,6 +308,13 @@ std::ostream& operator<<(std::ostream& out, const account_t& account); void put_account(property_tree::ptree& pt, const account_t& acct, function<bool(const account_t&)> pred); +//simple struct added to allow std::map to compare accounts in the accounts report +struct account_compare { + bool operator() (const account_t& lhs, const account_t& rhs){ + return (lhs.fullname().compare(rhs.fullname()) < 0); + } +}; + } // namespace ledger #endif // _ACCOUNT_H diff --git a/src/amount.cc b/src/amount.cc index 6ecb3558..ee03827e 100644 --- a/src/amount.cc +++ b/src/amount.cc @@ -195,7 +195,10 @@ namespace { for (const char * p = buf; *p; p++) { if (*p == '.') { - if (commodity_t::decimal_comma_by_default || + if (commodity_t::time_colon_by_default || + (comm && comm->has_flags(COMMODITY_STYLE_TIME_COLON))) + out << ':'; + else if (commodity_t::decimal_comma_by_default || (comm && comm->has_flags(COMMODITY_STYLE_DECIMAL_COMMA))) out << ','; else @@ -209,7 +212,10 @@ namespace { out << *p; if (integer_digits > 3 && --integer_digits % 3 == 0) { - if (commodity_t::decimal_comma_by_default || + if (commodity_t::time_colon_by_default || + (comm && comm->has_flags(COMMODITY_STYLE_TIME_COLON))) + out << ':'; + else if (commodity_t::decimal_comma_by_default || (comm && comm->has_flags(COMMODITY_STYLE_DECIMAL_COMMA))) out << '.'; else @@ -670,10 +676,27 @@ void amount_t::in_place_floor() _dup(); - std::ostringstream out; - stream_out_mpq(out, MP(quantity), precision_t(0), -1, GMP_RNDZ); + mpz_t quot; + mpz_init(quot); + mpz_fdiv_q(quot, mpq_numref(MP(quantity)), mpq_denref(MP(quantity))); + mpq_clear(MP(quantity)); + mpq_init(MP(quantity)); + mpq_set_num(MP(quantity), quot); +} + +void amount_t::in_place_ceiling() +{ + if (! quantity) + throw_(amount_error, _("Cannot ceiling an uninitialized amount")); - mpq_set_str(MP(quantity), out.str().c_str(), 10); + _dup(); + + mpz_t quot; + mpz_init(quot); + mpz_cdiv_q(quot, mpq_numref(MP(quantity)), mpq_denref(MP(quantity))); + mpq_clear(MP(quantity)); + mpq_init(MP(quantity)); + mpq_set_num(MP(quantity), quot); } void amount_t::in_place_unround() @@ -720,6 +743,16 @@ void amount_t::in_place_unreduce() } if (shifted) { + if ("h" == comm->symbol() && commodity_t::time_colon_by_default) { + amount_t floored = tmp.floored(); + amount_t precision = tmp - floored; + if (precision < 0.0) { + precision += 1.0; + floored -= 1.0; + } + tmp = floored + (precision * (comm->smaller()->number() / 100.0)); + } + *this = tmp; commodity_ = comm; } @@ -1073,6 +1106,9 @@ bool amount_t::parse(std::istream& in, const parse_flags_t& flags) bool decimal_comma_style = (commodity_t::decimal_comma_by_default || commodity().has_flags(COMMODITY_STYLE_DECIMAL_COMMA)); + bool time_colon_style + = (commodity_t::time_colon_by_default || + commodity().has_flags(COMMODITY_STYLE_TIME_COLON)); new_quantity->prec = 0; diff --git a/src/amount.h b/src/amount.h index 49027bb7..1b7d2101 100644 --- a/src/amount.h +++ b/src/amount.h @@ -364,6 +364,15 @@ public: } void in_place_floor(); + /** Yields an amount which has lost all of its extra precision, beyond what + the display precision of the commodity would have printed. */ + amount_t ceilinged() const { + amount_t temp(*this); + temp.in_place_ceiling(); + return temp; + } + void in_place_ceiling(); + /** Yields an amount whose display precision is never truncated, even though its commodity normally displays only rounded values. */ amount_t unrounded() const { diff --git a/src/balance.h b/src/balance.h index 230a4e2c..9635742d 100644 --- a/src/balance.h +++ b/src/balance.h @@ -345,6 +345,17 @@ public: pair.second.in_place_floor(); } + balance_t ceilinged() const { + balance_t temp(*this); + temp.in_place_ceiling(); + return temp; + } + void in_place_ceiling() { + foreach (amounts_map::value_type& pair, amounts) + pair.second.in_place_ceiling(); + } + + balance_t unrounded() const { balance_t temp(*this); temp.in_place_unround(); diff --git a/src/commodity.cc b/src/commodity.cc index 05d465ca..ffeac10d 100644 --- a/src/commodity.cc +++ b/src/commodity.cc @@ -40,6 +40,7 @@ namespace ledger { bool commodity_t::decimal_comma_by_default = false; +bool commodity_t::time_colon_by_default = false; void commodity_t::add_price(const datetime_t& date, const amount_t& price, const bool reflexive) diff --git a/src/commodity.h b/src/commodity.h index ab496850..82efac6a 100644 --- a/src/commodity.h +++ b/src/commodity.h @@ -107,6 +107,7 @@ protected: #define COMMODITY_SAW_ANNOTATED 0x200 #define COMMODITY_SAW_ANN_PRICE_FLOAT 0x400 #define COMMODITY_SAW_ANN_PRICE_FIXATED 0x800 +#define COMMODITY_STYLE_TIME_COLON 0x1000 string symbol; optional<std::size_t> graph_index; @@ -176,6 +177,7 @@ protected: public: static bool decimal_comma_by_default; + static bool time_colon_by_default; virtual ~commodity_t() { TRACE_DTOR(commodity_t); @@ -349,6 +351,13 @@ inline std::ostream& operator<<(std::ostream& out, const commodity_t& comm) { void put_commodity(property_tree::ptree& pt, const commodity_t& comm, bool commodity_details = false); +//simple struct to allow std::map to compare commodities names +struct commodity_compare { + bool operator() (const commodity_t* lhs, const commodity_t* rhs){ + return (lhs->symbol().compare(rhs->symbol()) < 0); + } +}; + } // namespace ledger #endif // _COMMODITY_H diff --git a/src/context.h b/src/context.h index 7373be39..9fe0613b 100644 --- a/src/context.h +++ b/src/context.h @@ -121,7 +121,7 @@ inline parse_context_t open_for_reading(const path& pathname, throw_(std::runtime_error, _f("Cannot read journal file %1%") % filename); - path parent(pathname.parent_path()); + path parent(filename.parent_path()); shared_ptr<std::istream> stream(new ifstream(filename)); parse_context_t context(stream, parent); context.pathname = filename; @@ -166,7 +166,7 @@ xact_t * csv_reader::read_xact(bool rich_data) string total; string field; - while (instr.good() && ! instr.eof()) { + while (instr.good() && ! instr.eof() && n < index.size()) { field = read_field(instr); switch (index[n]) { @@ -175,7 +175,8 @@ xact_t * csv_reader::read_xact(bool rich_data) break; case FIELD_DATE_AUX: - xact->_date_aux = parse_date(field); + if (! field.empty()) + xact->_date_aux = parse_date(field); break; case FIELD_CODE: @@ -224,7 +225,8 @@ xact_t * csv_reader::read_xact(bool rich_data) break; case FIELD_NOTE: - xact->note = field; + if (! field.empty()) + xact->note = field; break; case FIELD_UNKNOWN: @@ -243,7 +245,7 @@ xact_t * csv_reader::read_xact(bool rich_data) // Translate the account name, if we have enough information to do so - foreach (account_mapping_t& value, context.journal->account_mappings) { + foreach (account_mapping_t& value, context.journal->payees_for_unknown_accounts) { if (value.first.match(xact->payee)) { post->account = value.second; break; diff --git a/src/error.cc b/src/error.cc index 58339db7..8aa1d3d6 100644 --- a/src/error.cc +++ b/src/error.cc @@ -84,7 +84,7 @@ string source_context(const path& file, const string& prefix) { const std::streamoff len = end_pos - pos; - if (! len || file == path("/dev/stdin")) + if (! len || file.empty()) return _("<no source context>"); assert(len > 0); diff --git a/src/filters.cc b/src/filters.cc index f5694133..7570809a 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -530,9 +530,11 @@ bool display_filter_posts::output_rounding(post_t& post) } // Allow the posting to be displayed if: - // 1. It's display_amount would display as non-zero - // 2. The --empty option was specified - // 3. The account of the posting is <Revalued> + // 1. Its display_amount would display as non-zero, or + // 2. The --empty option was specified, or + // 3. a) The account of the posting is <Revalued>, and + // b) the revalued option is specified, and + // c) the --no-rounding option is not specified. if (post.account == revalued_account) { if (show_rounding) @@ -981,7 +983,8 @@ void interval_posts::flush() sort_posts_by_date()); // Determine the beginning interval by using the earliest post - if (! interval.find_period(all_posts.front()->date())) + if (all_posts.front() && + ! interval.find_period(all_posts.front()->date())) throw_(std::logic_error, _("Failed to find period for interval report")); // Walk the interval forward reporting all posts within each one diff --git a/src/global.cc b/src/global.cc index a718d6cb..5fc10f02 100644 --- a/src/global.cc +++ b/src/global.cc @@ -44,6 +44,7 @@ namespace ledger { static bool args_only = false; +std::string _init_file; global_scope_t::global_scope_t(char ** envp) { @@ -106,30 +107,53 @@ global_scope_t::~global_scope_t() #endif } +void global_scope_t::parse_init(path init_file) +{ + TRACE_START(init, 1, "Read initialization file"); + + parse_context_stack_t parsing_context; + parsing_context.push(init_file); + parsing_context.get_current().journal = session().journal.get(); + parsing_context.get_current().scope = &report(); + + if (session().journal->read(parsing_context) > 0 || + session().journal->auto_xacts.size() > 0 || + session().journal->period_xacts.size() > 0) { + throw_(parse_error, _f("Transactions found in initialization file '%1%'") + % init_file); + } + + TRACE_FINISH(init, 1); +} + void global_scope_t::read_init() { + // if specified on the command line init_file_ is filled in + // global_scope_t::handle_debug_options. If it was specified on the command line + // fail is the file doesn't exist. If no init file was specified + // on the command-line then try the default values, but don't fail if there + // isn't one. + path init_file; if (HANDLED(init_file_)) { - path init_file(HANDLER(init_file_).str()); + init_file=HANDLER(init_file_).str(); if (exists(init_file)) { - TRACE_START(init, 1, "Read initialization file"); - - parse_context_stack_t parsing_context; - parsing_context.push(init_file); - parsing_context.get_current().journal = session().journal.get(); - parsing_context.get_current().scope = &report(); - - if (session().journal->read(parsing_context) > 0 || - session().journal->auto_xacts.size() > 0 || - session().journal->period_xacts.size() > 0) { - throw_(parse_error, _f("Transactions found in initialization file '%1%'") - % init_file); - } - - TRACE_FINISH(init, 1); + parse_init(init_file); + } else { + throw_(parse_error, _f("Could not find specified init file %1%") % init_file); } + } else { + if (const char * home_var = std::getenv("HOME")){ + init_file = (path(home_var) / ".ledgerrc"); + } else { + init_file = ("./.ledgerrc"); + } + } + if(exists(init_file)){ + parse_init(init_file); } } + char * global_scope_t::prompt_string() { static char prompt[32]; @@ -473,6 +497,10 @@ void handle_debug_options(int argc, char * argv[]) _log_level = LOG_INFO; #endif } + else if (i + 1 < argc && std::strcmp(argv[i], "--init-file") == 0) { + _init_file = argv[i + 1]; + i++; + } else if (i + 1 < argc && std::strcmp(argv[i], "--debug") == 0) { #if DEBUG_ON _log_level = LOG_DEBUG; diff --git a/src/global.h b/src/global.h index f797ba01..11459529 100644 --- a/src/global.h +++ b/src/global.h @@ -46,6 +46,8 @@ namespace ledger { class session_t; class report_t; +extern std::string _init_file; + class global_scope_t : public noncopyable, public scope_t { shared_ptr<session_t> session_ptr; @@ -65,6 +67,7 @@ public: return _("global scope"); } + void parse_init(path init_file); void read_init(); void read_environment_settings(char * envp[]); strings_list read_command_arguments(scope_t& scope, strings_list args); @@ -151,10 +154,9 @@ See LICENSE file included with the distribution for details and disclaimer."); OPTION__ (global_scope_t, init_file_, // -i CTOR(global_scope_t, init_file_) { - if (const char * home_var = std::getenv("HOME")) - on(none, (path(home_var) / ".ledgerrc").string()); - else - on(none, path("./.ledgerrc").string()); + if (!_init_file.empty()) + // _init_file is filled during handle_debug_options + on(none, _init_file); }); OPTION(global_scope_t, options); diff --git a/src/item.cc b/src/item.cc index 0630043b..4e2a487c 100644 --- a/src/item.cc +++ b/src/item.cc @@ -330,6 +330,21 @@ namespace { return NULL_VALUE; } + value_t get_filebase(item_t& item) { + if (item.pos) + return string_value(item.pos->pathname.filename().string()); + else + return NULL_VALUE; + } + + value_t get_filepath(item_t& item) { + if (item.pos) + return string_value(item.pos->pathname.parent_path().string()); + else + return NULL_VALUE; + } + + value_t get_beg_pos(item_t& item) { return item.pos ? long(item.pos->beg_pos) : 0L; } @@ -456,7 +471,11 @@ expr_t::ptr_op_t item_t::lookup(const symbol_t::kind_t kind, case 'f': if (name == "filename") return WRAP_FUNCTOR(get_wrapper<&get_pathname>); - break; + else if (name == "filebase") + return WRAP_FUNCTOR(get_wrapper<&get_filebase>); + else if (name == "filepath") + return WRAP_FUNCTOR(get_wrapper<&get_filepath>); + break; case 'h': if (name == "has_tag") @@ -563,8 +582,8 @@ string item_context(const item_t& item, const string& desc) std::ostringstream out; - if (item.pos->pathname == path("/dev/stdin")) { - out << desc << _(" from standard input:"); + if (item.pos->pathname.empty()) { + out << desc << _(" from streamed input:"); return out.str(); } diff --git a/src/journal.cc b/src/journal.cc index e6c09125..68939be6 100644 --- a/src/journal.cc +++ b/src/journal.cc @@ -127,8 +127,19 @@ account_t * journal_t::register_account(const string& name, post_t * post, // object. if (account_aliases.size() > 0) { accounts_map::const_iterator i = account_aliases.find(name); - if (i != account_aliases.end()) + if (i != account_aliases.end()) { result = (*i).second; + } else { + // only check the very first account for alias expansion, in case + // that can be expanded successfully + size_t colon = name.find(':'); + if(colon != string::npos) { + accounts_map::const_iterator i = account_aliases.find(name.substr(0, colon)); + if (i != account_aliases.end()) { + result = find_account((*i).second->fullname() + name.substr(colon)); + } + } + } } // Create the account object and associate it with the journal; this diff --git a/src/main.cc b/src/main.cc index a1ac0339..a44506c9 100644 --- a/src/main.cc +++ b/src/main.cc @@ -59,6 +59,7 @@ int main(int argc, char * argv[], char * envp[]) // --debug CATEGORY ; turns on debug logging // --trace LEVEL ; turns on trace logging // --memory ; turns on memory usage tracing + // --init-file ; directs ledger to use a different init file handle_debug_options(argc, argv); #if VERIFY_ON IF_VERIFY() initialize_memory_tracing(); @@ -213,7 +214,8 @@ int main(int argc, char * argv[], char * envp[]) } else #endif { - global_scope->quick_close(); + if (global_scope) + global_scope->quick_close(); INFO("Ledger ended"); // let global_scope leak! } diff --git a/src/output.cc b/src/output.cc index f433f8d1..6ed7f861 100644 --- a/src/output.cc +++ b/src/output.cc @@ -318,6 +318,34 @@ void report_payees::operator()(post_t& post) (*i).second++; } +void report_tags::flush() +{ + std::ostream& out(report.output_stream); + + foreach (tags_pair& entry, tags) { + if (report.HANDLED(count)) + out << entry.second << ' '; + out << entry.first << '\n'; + } +} + +void report_tags::operator()(post_t& post) +{ + if(post.metadata){ + foreach (const item_t::string_map::value_type& data, *post.metadata){ + string tag=data.first; + if(report.HANDLED(values) && (data.second).first){ + tag+=": "+ (data.second).first.get().to_string(); + } + std::map<string, std::size_t>::iterator i = tags.find(tag); + if (i == tags.end()) + tags.insert(tags_pair(tag, 1)); + else + (*i).second++; + } + } +} + void report_commodities::flush() { std::ostream& out(report.output_stream); diff --git a/src/output.h b/src/output.h index 281f69b6..44eca2d2 100644 --- a/src/output.h +++ b/src/output.h @@ -142,7 +142,7 @@ class report_accounts : public item_handler<post_t> protected: report_t& report; - std::map<account_t *, std::size_t> accounts; + std::map<account_t *, std::size_t, account_compare> accounts; typedef std::map<account_t *, std::size_t>::value_type accounts_pair; @@ -189,12 +189,39 @@ public: } }; +class report_tags : public item_handler<post_t> +{ +protected: + report_t& report; + + std::map<string, std::size_t> tags; + + typedef std::map<string, std::size_t>::value_type tags_pair; + +public: + report_tags(report_t& _report) : report(_report) { + TRACE_CTOR(report_tags, "report&"); + } + virtual ~report_tags() { + TRACE_DTOR(report_tags); + } + + virtual void flush(); + virtual void operator()(post_t& post); + + virtual void clear() { + tags.clear(); + item_handler<post_t>::clear(); + } +}; + + class report_commodities : public item_handler<post_t> { protected: report_t& report; - std::map<commodity_t *, std::size_t> commodities; + std::map<commodity_t *, std::size_t, commodity_compare> commodities; typedef std::map<commodity_t *, std::size_t>::value_type commodities_pair; @@ -83,7 +83,7 @@ public: const optional<string>& _note = none) : item_t(_flags, _note), xact(NULL), account(_account), amount(_amount) { - TRACE_CTOR(post_t, "account_t *, const amount_t&, flags_t, const optional<string>&"); + TRACE_CTOR(post_t, "account_t *, amount_t, flags_t, optional<string>"); } post_t(const post_t& post) : item_t(post), diff --git a/src/print.cc b/src/print.cc index 79d83161..a4a0bc6f 100644 --- a/src/print.cc +++ b/src/print.cc @@ -203,9 +203,15 @@ namespace { (static_cast<std::string::size_type>(account_width) - static_cast<std::string::size_type>(name.length())); + std::size_t amount_width = + (report.HANDLED(amount_width_) ? + lexical_cast<std::size_t>(report.HANDLER(amount_width_).str()) : + 12); string amt; if (post->amount_expr) { - amt = post->amount_expr->text(); + std::ostringstream amt_str; + justify(amt_str, post->amount_expr->text(), amount_width, true); + amt = amt_str.str(); } else if (count == 2 && index == 2 && post_has_simple_amount(*post) && @@ -218,11 +224,6 @@ namespace { // first. } else { - std::size_t amount_width = - (report.HANDLED(amount_width_) ? - lexical_cast<std::size_t>(report.HANDLER(amount_width_).str()) : - 12); - std::ostringstream amt_str; value_t(post->amount).print(amt_str, static_cast<int>(amount_width), -1, AMOUNT_PRINT_RIGHT_JUSTIFY | diff --git a/src/py_xact.cc b/src/py_xact.cc index 3d792c7b..96674207 100644 --- a/src/py_xact.cc +++ b/src/py_xact.cc @@ -119,8 +119,8 @@ void export_xact() .def("__str__", py_xact_to_string) .add_property("code", - make_getter(&xact_t::code), - make_setter(&xact_t::code)) + make_getter(&xact_t::code, return_value_policy<return_by_value>()), + make_setter(&xact_t::code, return_value_policy<return_by_value>())) .add_property("payee", make_getter(&xact_t::payee), make_setter(&xact_t::payee)) @@ -157,6 +157,8 @@ void export_xact() make_getter(&period_xact_t::period_string), make_setter(&period_xact_t::period_string)) ; + + register_optional_to_python<std::string>(); } } // namespace ledger diff --git a/src/report.cc b/src/report.cc index 662386a4..80993515 100644 --- a/src/report.cc +++ b/src/report.cc @@ -681,6 +681,11 @@ value_t report_t::fn_floor(call_scope_t& args) return args[0].floored(); } +value_t report_t::fn_ceiling(call_scope_t& args) +{ + return args[0].ceilinged(); +} + value_t report_t::fn_round(call_scope_t& args) { return args[0].rounded(); @@ -1084,6 +1089,7 @@ option_t<report_t> * report_t::lookup_option(const char * p) else OPT(anon); else OPT_ALT(color, ansi); else OPT(auto_match); + else OPT(aux_date); else OPT(average); else OPT(account_width_); else OPT(amount_width_); @@ -1091,7 +1097,7 @@ option_t<report_t> * report_t::lookup_option(const char * p) case 'b': OPT(balance_format_); else OPT(base); - else OPT_ALT(basis, cost); + else OPT(basis); else OPT_(begin_); else OPT(bold_if_); else OPT(budget); @@ -1100,6 +1106,7 @@ option_t<report_t> * report_t::lookup_option(const char * p) break; case 'c': OPT(csv_format_); + else OPT_ALT(gain, change); else OPT(cleared); else OPT(collapse); else OPT(collapse_if_zero); @@ -1117,6 +1124,7 @@ option_t<report_t> * report_t::lookup_option(const char * p) else OPT(dc); else OPT(depth_); else OPT(deviation); + else OPT_ALT(rich_data, detail); else OPT_(display_); else OPT(display_amount_); else OPT(display_total_); @@ -1141,7 +1149,7 @@ option_t<report_t> * report_t::lookup_option(const char * p) else OPT_ALT(head_, first_); break; case 'g': - OPT_ALT(gain, change); + OPT(gain); else OPT(group_by_); else OPT(group_title_format_); else OPT(generated); @@ -1168,7 +1176,7 @@ option_t<report_t> * report_t::lookup_option(const char * p) else OPT_ALT(tail_, last_); break; case 'm': - OPT_ALT(market, value); + OPT(market); else OPT(monthly); else OPT(meta_); else OPT(meta_width_); @@ -1198,6 +1206,7 @@ option_t<report_t> * report_t::lookup_option(const char * p) else OPT(price); else OPT(prices_format_); else OPT(pricedb_format_); + else OPT(primary_date); else OPT(payee_width_); else OPT(prepend_format_); else OPT(prepend_width_); @@ -1215,7 +1224,7 @@ option_t<report_t> * report_t::lookup_option(const char * p) else OPT(revalued); else OPT(revalued_only); else OPT(revalued_total_); - else OPT_ALT(rich_data, detail); + else OPT(rich_data); break; case 's': OPT(sort_); @@ -1242,6 +1251,10 @@ option_t<report_t> * report_t::lookup_option(const char * p) else OPT(unrealized_losses_); else OPT(unround); break; + case 'v': + OPT_ALT(market, value); + else OPT(values); + break; case 'w': OPT(weekly); else OPT_(wide); @@ -1335,6 +1348,8 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, return WRAP_FUNCTOR(fn_cyan); else if (is_eq(p, "commodity")) return MAKE_FUNCTOR(report_t::fn_commodity); + else if (is_eq(p, "ceiling")) + return MAKE_FUNCTOR(report_t::fn_ceiling); break; case 'd': @@ -1585,7 +1600,11 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, return POSTS_REPORTER(new report_commodities(*this)); } break; - + case 'd': + if (is_eq(p, "draft")) { + return WRAP_FUNCTOR(xact_command); + } + break; case 'e': if (is_eq(p, "equity")) { HANDLER(generated).on("#equity"); @@ -1607,7 +1626,10 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, return POSTS_REPORTER(new format_ptree(*this, format_ptree::FORMAT_JSON)); break; - + case 'l': + if (is_eq(p, "lisp")) + return POSTS_REPORTER(new format_emacs_posts(output_stream)); + break; case 'o': if (is_eq(p, "org")) return POSTS_REPORTER(new posts_to_org_table @@ -1649,7 +1671,11 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, else if (is_eq(p, "select")) return WRAP_FUNCTOR(select_command); break; - + case 't': + if (is_eq(p, "tags")) { + return POSTS_REPORTER(new report_tags(*this)); + } + break; case 'x': if (is_eq(p, "xact")) return WRAP_FUNCTOR(xact_command); diff --git a/src/report.h b/src/report.h index 4a02843e..b0044f60 100644 --- a/src/report.h +++ b/src/report.h @@ -174,6 +174,7 @@ public: value_t fn_unrounded(call_scope_t& scope); value_t fn_truncated(call_scope_t& scope); value_t fn_floor(call_scope_t& scope); + value_t fn_ceiling(call_scope_t& scope); value_t fn_round(call_scope_t& scope); value_t fn_unround(call_scope_t& scope); value_t fn_abs(call_scope_t& scope); @@ -357,6 +358,7 @@ public: HANDLER(account_width_).report(out); HANDLER(amount_width_).report(out); HANDLER(total_width_).report(out); + HANDLER(values).report(out); } option_t<report_t> * lookup_option(const char * p); @@ -1042,6 +1044,7 @@ public: OPTION(report_t, account_width_); OPTION(report_t, amount_width_); OPTION(report_t, total_width_); + OPTION(report_t, values); }; template <class Type = post_t, diff --git a/src/session.cc b/src/session.cc index b6153203..632002d4 100644 --- a/src/session.cc +++ b/src/session.cc @@ -98,8 +98,12 @@ std::size_t session_t::read_data(const string& master_account) acct = journal->find_account(master_account); optional<path> price_db_path; - if (HANDLED(price_db_)) + if (HANDLED(price_db_)){ price_db_path = resolve_path(HANDLER(price_db_).str()); + if (!exists(price_db_path.get())){ + throw_(parse_error, _f("Could not find specified price file %1%") % price_db_path); + } + } if (HANDLED(explicit)) journal->force_checking = true; @@ -143,7 +147,7 @@ std::size_t session_t::read_data(const string& master_account) } foreach (const path& pathname, HANDLER(file_).data_files) { - if (pathname == "-") { + if (pathname == "-" || pathname == "/dev/stdin") { // To avoid problems with stdin and pipes, etc., we read the entire // file in beforehand into a memory buffer, and then parcel it out // from there. @@ -348,9 +352,11 @@ option_t<session_t> * session_t::lookup_option(const char * p) case 's': OPT(strict); break; + case 't': + OPT(time_colon); + break; case 'v': OPT(value_expr_); - break; } return NULL; } diff --git a/src/session.h b/src/session.h index a0aba91b..74aeab5f 100644 --- a/src/session.h +++ b/src/session.h @@ -100,6 +100,7 @@ public: HANDLER(day_break).report(out); HANDLER(download).report(out); HANDLER(decimal_comma).report(out); + HANDLER(time_colon).report(out); HANDLER(file_).report(out); HANDLER(input_date_format_).report(out); HANDLER(explicit).report(out); @@ -130,6 +131,10 @@ public: commodity_t::decimal_comma_by_default = true; }); + OPTION_(session_t, time_colon, DO() { + commodity_t::time_colon_by_default = true; + }); + OPTION__ (session_t, price_exp_, // -Z CTOR(session_t, price_exp_) { value = "24"; }); diff --git a/src/textual.cc b/src/textual.cc index 0ead9232..6106914f 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -148,7 +148,7 @@ namespace { void account_value_directive(account_t * account, string expr_str); void account_default_directive(account_t * account); - void default_account_directive(char * line); + void default_account_directive(char * args); void alias_directive(char * line); void payee_directive(char * line); @@ -397,7 +397,7 @@ void instance_t::read_next_directive(bool& error_flag) #endif // TIMELOG_SUPPORT case 'A': // a default account for unbalanced posts - default_account_directive(line); + default_account_directive(line + 1); break; case 'C': // a set of conversions price_conversion_directive(line); @@ -492,7 +492,7 @@ void instance_t::default_commodity_directive(char * line) void instance_t::default_account_directive(char * line) { - context.journal->bucket = top_account()->find_account(skip_ws(line + 1)); + context.journal->bucket = top_account()->find_account(skip_ws(line)); context.journal->bucket->add_flags(ACCOUNT_KNOWN); } @@ -575,7 +575,7 @@ void instance_t::automated_xact_directive(char * line) item = ae.get(); // This is a trailing note, and possibly a metadata info tag - item->append_note(p + 1, *context.scope, true); + ae->append_note(p + 1, *context.scope, true); item->add_flags(ITEM_NOTE_ON_NEXT_LINE); item->pos->end_pos = context.curr_pos; item->pos->end_line++; diff --git a/src/value.cc b/src/value.cc index 1921d5a3..c57cff78 100644 --- a/src/value.cc +++ b/src/value.cc @@ -1658,6 +1658,29 @@ void value_t::in_place_floor() throw_(value_error, _f("Cannot floor %1%") % label()); } +void value_t::in_place_ceiling() +{ + switch (type()) { + case INTEGER: + return; + case AMOUNT: + as_amount_lval().in_place_ceiling(); + return; + case BALANCE: + as_balance_lval().in_place_ceiling(); + return; + case SEQUENCE: + foreach (value_t& value, as_sequence_lval()) + value.in_place_ceiling(); + return; + default: + break; + } + + add_error_context(_f("While ceiling %1%:") % *this); + throw_(value_error, _f("Cannot ceiling %1%") % label()); +} + void value_t::in_place_unround() { switch (type()) { diff --git a/src/value.h b/src/value.h index ee3d414d..249f3d7f 100644 --- a/src/value.h +++ b/src/value.h @@ -457,6 +457,13 @@ public: } void in_place_floor(); + value_t ceilinged() const { + value_t temp(*this); + temp.in_place_ceiling(); + return temp; + } + void in_place_ceiling(); + value_t unrounded() const { value_t temp(*this); temp.in_place_unround(); diff --git a/src/xact.cc b/src/xact.cc index a54da81a..7ac7a9e9 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -36,6 +36,7 @@ #include "account.h" #include "journal.h" #include "context.h" +#include "format.h" #include "pool.h" namespace ledger { @@ -644,6 +645,18 @@ namespace { } } +static string apply_format(const string& str, scope_t& scope) +{ + if (contains(str, "%(")) { + format_t str_format(str); + std::ostringstream buf; + buf << str_format(scope); + return buf.str(); + } else { + return str; + } +} + void auto_xact_t::extend_xact(xact_base_t& xact, parse_context_t& context) { posts_list initial_posts(xact.posts.begin(), xact.posts.end()); @@ -695,8 +708,9 @@ void auto_xact_t::extend_xact(xact_base_t& xact, parse_context_t& context) if (deferred_notes) { foreach (deferred_tag_data_t& data, *deferred_notes) { if (data.apply_to_post == NULL) - initial_post->parse_tags(data.tag_data.c_str(), bound_scope, - data.overwrite_existing); + initial_post->append_note( + apply_format(data.tag_data, bound_scope).c_str(), + bound_scope, data.overwrite_existing); } } @@ -775,11 +789,27 @@ void auto_xact_t::extend_xact(xact_base_t& xact, parse_context_t& context) account = account->parent; account = account->find_account(fullname); } + else if (contains(fullname, "%(")) { + format_t account_name(fullname); + std::ostringstream buf; + buf << account_name(bound_scope); + while (account->parent) + account = account->parent; + account = account->find_account(buf.str()); + } // Copy over details so that the resulting post is a mirror of // the automated xact's one. post_t * new_post = new post_t(account, amt); new_post->copy_details(*post); + + // A Cleared transaction implies all of its automatic posting are cleared + // CPR 2012/10/23 + if (xact.state() == item_t::CLEARED) { + DEBUG("xact.extend.cleared", "CLEARED"); + new_post->set_state(item_t::CLEARED); + } + new_post->add_flags(ITEM_GENERATED); new_post->account = journal->register_account(account->fullname(), new_post, @@ -787,9 +817,11 @@ void auto_xact_t::extend_xact(xact_base_t& xact, parse_context_t& context) if (deferred_notes) { foreach (deferred_tag_data_t& data, *deferred_notes) { - if (! data.apply_to_post || data.apply_to_post == post) - new_post->parse_tags(data.tag_data.c_str(), bound_scope, - data.overwrite_existing); + if (! data.apply_to_post || data.apply_to_post == post) { + new_post->append_note( + apply_format(data.tag_data, bound_scope).c_str(), + bound_scope, data.overwrite_existing); + } } } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index b5d8cf09..94ce0a0a 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -22,7 +22,7 @@ macro(add_ledger_harness_tests _class) foreach(TestFile ${${_class}_TESTS}) get_filename_component(TestFile_Name ${TestFile} NAME_WE) string(FIND ${TestFile_Name} "_py" TestFile_IsPythonTest) - if((NOT TestFile_IsPythonTest) OR HAVE_BOOST_PYTHON) + if((TestFile_IsPythonTest EQUAL -1) OR HAVE_BOOST_PYTHON) add_test(${_class}Test_${TestFile_Name} ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/test/RegressTests.py ${LEDGER_LOCATION} ${PROJECT_SOURCE_DIR} diff --git a/test/LedgerHarness.py b/test/LedgerHarness.py index b8900971..39a31286 100755 --- a/test/LedgerHarness.py +++ b/test/LedgerHarness.py @@ -116,12 +116,14 @@ class LedgerHarness: def success(self): sys.stdout.write(".") + sys.stdout.flush() self.succeeded += 1 def failure(self, name=None): sys.stdout.write("E") if name: sys.stdout.write("[%s]" % name) + sys.stdout.flush() self.failed += 1 def exit(self): diff --git a/test/RegressTests.py b/test/RegressTests.py index 7d67eb21..01e14191 100755 --- a/test/RegressTests.py +++ b/test/RegressTests.py @@ -98,8 +98,10 @@ class RegressFile(object): def run_test(self, test): use_stdin = False - if test['command'].find("-f - ") != -1: - use_stdin = True + if test['command'].find("-f ") != -1: + test['command'] = '$ledger ' + test['command'] + if re.search("-f (-|/dev/stdin)(\s|$)", test['command']): + use_stdin = True else: test['command'] = (('$ledger -f "%s" ' % os.path.abspath(self.filename)) + diff --git a/test/baseline/cmd-accounts.test b/test/baseline/cmd-accounts.test index be6365fd..2f0310da 100644 --- a/test/baseline/cmd-accounts.test +++ b/test/baseline/cmd-accounts.test @@ -15,12 +15,12 @@ Assets:Testing123ÕßDone test accounts +Assets:AAA Assets:Bank -Equity:Opening balance +Assets:Testing123ÕßDone Assets:XXX -Assets:AAA Assets:♚ -Assets:Testing123ÕßDone +Equity:Opening balance end test test accounts assets:a diff --git a/test/baseline/cmd-commodities.test b/test/baseline/cmd-commodities.test index 0ce6f7a0..719b6798 100644 --- a/test/baseline/cmd-commodities.test +++ b/test/baseline/cmd-commodities.test @@ -15,10 +15,10 @@ Income:Rewards test commodities -GBP -AAA "AA2" "M&M" +AAA +GBP end test test commodities Assets:Rewards diff --git a/test/baseline/cmd-convert.test b/test/baseline/cmd-convert.test index 1c36d4bb..d444da52 100644 --- a/test/baseline/cmd-convert.test +++ b/test/baseline/cmd-convert.test @@ -4,7 +4,7 @@ test -f /dev/null --input-date-format "%m/%d/%Y" convert test/baseline/cmd-conve Expenses:Unknown $10 Equity:Unknown $-10 = $20 -2011/12/12=2011/12/12 * ; +2011/12/12=2011/12/12 * Expenses:Unknown $10 Equity:Unknown end test diff --git a/test/baseline/feat-convert-with-diretives.dat b/test/baseline/feat-convert-with-diretives.dat new file mode 100644 index 00000000..ac13ff81 --- /dev/null +++ b/test/baseline/feat-convert-with-diretives.dat @@ -0,0 +1,3 @@ +date,payee,amount +2012/01/01,KFC,$10 +2012/01/02,"REWE SAGT DANKE 123454321",10€ diff --git a/test/baseline/feat-convert-with-diretives.test b/test/baseline/feat-convert-with-diretives.test new file mode 100644 index 00000000..2f6e0102 --- /dev/null +++ b/test/baseline/feat-convert-with-diretives.test @@ -0,0 +1,28 @@ +account Expenses:Food + payee KFC + payee REWE + +payee REWE + alias REWE SAGT DANKE + +# When reading csv file without directives: +test -f /dev/null convert test/baseline/feat-convert-with-diretives.dat +2012/01/01 * KFC + Expenses:Unknown $10 + Equity:Unknown + +2012/01/02 * REWE SAGT DANKE 123454321 + Expenses:Unknown 10€ + Equity:Unknown +end test + +# When reading csv file with directives: +test --account "Assets:Cash" convert test/baseline/feat-convert-with-diretives.dat +2012/01/01 * KFC + Expenses:Food $10 + Assets:Cash + +2012/01/02 * REWE + Expenses:Food 10€ + Assets:Cash +end test diff --git a/test/baseline/opt-aux-date.test b/test/baseline/opt-aux-date.test index 9d1e73d0..495bb7e6 100644 --- a/test/baseline/opt-aux-date.test +++ b/test/baseline/opt-aux-date.test @@ -10,7 +10,7 @@ Expenses:Books $20.00 Assets:Cash -test reg --effective +test reg --aux-date 08-Jan-01 January Expenses:Books $10.00 $10.00 Assets:Cash $-10.00 0 08-Feb-01 End of January Expenses:Books $10.00 $10.00 diff --git a/test/baseline/opt-count.test b/test/baseline/opt-count.test index 9c5495c8..7c935c7a 100644 --- a/test/baseline/opt-count.test +++ b/test/baseline/opt-count.test @@ -17,14 +17,14 @@ Assets:Cash -30.00 EUR test accounts --count -2 Expenses:Phone 4 Assets:Cash +2 Expenses:Phone 2 Expenses:Rent end test test commodities --count -4 GBP 4 EUR +4 GBP end test test payees --count @@ -33,8 +33,8 @@ test payees --count end test test commodities :rent --count -1 GBP 1 EUR +1 GBP end test test payees tag bnb --count diff --git a/test/regress/0161EB1E.test b/test/regress/0161EB1E.test new file mode 100644 index 00000000..93498ad5 --- /dev/null +++ b/test/regress/0161EB1E.test @@ -0,0 +1,15 @@ +bucket Assets:Checking +2011/04/25 Tom's Used Cars + Auto $ 5,500.00 + ; :nobudget: + +A Assets:Checking +2011/04/27 Book Store + Books $20.00 + +test reg +11-Apr-25 Tom's Used Cars Auto $ 5,500.00 $ 5,500.00 + Assets:Checking $ -5,500.00 0 +11-Apr-27 Book Store Books $ 20.00 $ 20.00 + Assets:Checking $ -20.00 0 +end test diff --git a/test/regress/0DDDEBC0.dat b/test/regress/0DDDEBC0.dat new file mode 100644 index 00000000..21b6fce1 --- /dev/null +++ b/test/regress/0DDDEBC0.dat @@ -0,0 +1,3 @@ +date,posted,amount, +12/12/2011,12/13/2011,$10, +12/12/2011,,$20, diff --git a/test/regress/0DDDEBC0.test b/test/regress/0DDDEBC0.test new file mode 100644 index 00000000..cccb1b6b --- /dev/null +++ b/test/regress/0DDDEBC0.test @@ -0,0 +1,9 @@ +test -f /dev/null --input-date-format '%m/%d/%Y' convert test/regress/0DDDEBC0.dat +2011/12/12=2011/12/13 * + Expenses:Unknown $10 + Equity:Unknown + +2011/12/12 * + Expenses:Unknown $20 + Equity:Unknown +end test diff --git a/test/regress/25A099C9.test b/test/regress/25A099C9.test index d4eab662..fb362a4b 100644 --- a/test/regress/25A099C9.test +++ b/test/regress/25A099C9.test @@ -20,24 +20,24 @@ While parsing file "$sourcepath/src/amount.h", line 121: Error: Unexpected whitespace at beginning of line While parsing file "$sourcepath/src/amount.h", line 132: Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 702: +While parsing file "$sourcepath/src/amount.h", line 711: Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 732: +While parsing file "$sourcepath/src/amount.h", line 741: Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 740: +While parsing file "$sourcepath/src/amount.h", line 749: Error: Unexpected whitespace at beginning of line -While parsing file "$sourcepath/src/amount.h", line 743: +While parsing file "$sourcepath/src/amount.h", line 752: Error: Invalid date/time: line amount_t amoun -While parsing file "$sourcepath/src/amount.h", line 749: +While parsing file "$sourcepath/src/amount.h", line 758: Error: Invalid date/time: line string amount_ -While parsing file "$sourcepath/src/amount.h", line 755: +While parsing file "$sourcepath/src/amount.h", line 764: Error: Invalid date/time: line string amount_ -While parsing file "$sourcepath/src/amount.h", line 761: +While parsing file "$sourcepath/src/amount.h", line 770: Error: Invalid date/time: line string amount_ -While parsing file "$sourcepath/src/amount.h", line 767: +While parsing file "$sourcepath/src/amount.h", line 776: Error: Invalid date/time: line std::ostream& -While parsing file "$sourcepath/src/amount.h", line 774: +While parsing file "$sourcepath/src/amount.h", line 783: Error: Invalid date/time: line std::istream& -While parsing file "$sourcepath/src/amount.h", line 780: +While parsing file "$sourcepath/src/amount.h", line 789: Error: Unexpected whitespace at beginning of line end test diff --git a/test/regress/68917252.dat b/test/regress/68917252.dat new file mode 100644 index 00000000..8857f741 --- /dev/null +++ b/test/regress/68917252.dat @@ -0,0 +1,2 @@ +date,amount, +12/12/2011,10,test,extra,fields diff --git a/test/regress/68917252.test b/test/regress/68917252.test new file mode 100644 index 00000000..52fde1d9 --- /dev/null +++ b/test/regress/68917252.test @@ -0,0 +1,5 @@ +test -f /dev/null --input-date-format "%m/%d/%Y" convert test/regress/68917252.dat +2011/12/12 * + Expenses:Unknown 10 + Equity:Unknown +end test diff --git a/test/regress/A3FA7601.dat b/test/regress/A3FA7601.dat new file mode 100644 index 00000000..9a734e67 --- /dev/null +++ b/test/regress/A3FA7601.dat @@ -0,0 +1,3 @@ +date,posted,code,payee,amount,total,note, +12/12/2011,12/13/2011,100,Test,$10,$20,test, +12/12/2011,12/12/2011,,,$10,$20, diff --git a/test/regress/A3FA7601.test b/test/regress/A3FA7601.test new file mode 100644 index 00000000..5029f6d8 --- /dev/null +++ b/test/regress/A3FA7601.test @@ -0,0 +1,9 @@ +test -f /dev/null --input-date-format '%m/%d/%Y' convert test/regress/A3FA7601.dat +2011/12/12=2011/12/13 * (100) Test ;test + Expenses:Unknown $10 + Equity:Unknown $-10 = $20 + +2011/12/12=2011/12/12 * + Expenses:Unknown $10 + Equity:Unknown $-10 = $20 +end test diff --git a/test/regress/BF3C1F82-2.test b/test/regress/BF3C1F82-2.test new file mode 100644 index 00000000..453151ce --- /dev/null +++ b/test/regress/BF3C1F82-2.test @@ -0,0 +1,12 @@ +; Check that include directives are relative for "-f /dev/stdin" +include non-existent-ledger-file-BF3C1F82 +test -f - reg -> 1 +__ERROR__ +While parsing file "", line 2: +Error: File to include was not found: "./non-existent-ledger-file-BF3C1F82" +end test +test -f /dev/stdin reg -> 1 +__ERROR__ +While parsing file "", line 2: +Error: File to include was not found: "./non-existent-ledger-file-BF3C1F82" +end test diff --git a/test/regress/BF3C1F82.test b/test/regress/BF3C1F82.test new file mode 100644 index 00000000..50f4106f --- /dev/null +++ b/test/regress/BF3C1F82.test @@ -0,0 +1,19 @@ +; Check that error reporting works for "-f -" + +2012/02/30 * Test + a 1 + b +test -f - reg -> 1 +__ERROR__ +While parsing file "", line 3: +While parsing transaction: +<no source context> +Error: Day of month is not valid for year +end test +test -f /dev/stdin reg -> 1 +__ERROR__ +While parsing file "", line 3: +While parsing transaction: +<no source context> +Error: Day of month is not valid for year +end test diff --git a/test/regress/CAE63F5C-a.test b/test/regress/CAE63F5C-a.test new file mode 100644 index 00000000..4465bd2f --- /dev/null +++ b/test/regress/CAE63F5C-a.test @@ -0,0 +1,17 @@ +2011/03/01 test1 + a 4.00 € + b + +2011/03/02 test2 + a 4.00 € + b + +2011/03/03 test2 + a 4.00 € + b + +test reg a +11-Mar-01 test1 a 4.00 € 4.00 € +11-Mar-02 test2 a 4.00 € 8.00 € +11-Mar-03 test2 a 4.00 € 12.00 € +end test diff --git a/test/regress/CAE63F5C-b.test b/test/regress/CAE63F5C-b.test new file mode 100644 index 00000000..c0b7efd8 --- /dev/null +++ b/test/regress/CAE63F5C-b.test @@ -0,0 +1,15 @@ +2012/08/22 Payment + Accrued €208.00 {=$1.3109} @ $1.2799 + Expenses €4.16 {=$1.2798689} @ $1.2799 + Assets $-271.54 + Income:Currency Conversion $-6.45 + +test bal -X $ + $272.67 Accrued + $-271.54 Assets + $6.45 Equity:Capital Gains + $5.32 Expenses + $-6.45 Income:Currency Conversion +-------------------- + $6.45 +end test diff --git a/test/regress/CAE63F5C-c.test b/test/regress/CAE63F5C-c.test new file mode 100644 index 00000000..ae2d7d10 --- /dev/null +++ b/test/regress/CAE63F5C-c.test @@ -0,0 +1,15 @@ +2012/08/22 Payment + Accrued €208.00 {=$1.3109} @ $1.2798689 + Expenses €4.16 {=$1.2798689} @ $1.2798689 + Assets $-271.54 + Income:Currency Conversion $-6.45 + +test bal -X $ + $272.67 Accrued + $-271.54 Assets + $6.45 Equity:Capital Gains + $5.32 Expenses + $-6.45 Income:Currency Conversion +-------------------- + $6.45 +end test diff --git a/test/regress/CMakeLists.txt b/test/regress/CMakeLists.txt index 4b6232dd..26f55e84 100644 --- a/test/regress/CMakeLists.txt +++ b/test/regress/CMakeLists.txt @@ -1,19 +1 @@ -if(HAVE_BOOST_PYTHON) - set(TEST_PYTHON_FLAGS "--python") -endif() - -if(PYTHONINTERP_FOUND) - file(GLOB REGRESSION_TESTS *.test) - foreach(TestFile ${REGRESSION_TESTS}) - get_filename_component(TestFile_Name ${TestFile} NAME_WE) - string(FIND ${TestFile_Name} "_py" TestFile_IsPythonTest) - if((NOT TestFile_IsPythonTest) OR HAVE_BOOST_PYTHON) - add_test(RegressionTest_${TestFile_Name} - ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/test/RegressTests.py - ${LEDGER_LOCATION} ${PROJECT_SOURCE_DIR} - ${TestFile} ${TEST_PYTHON_FLAGS}) - set_target_properties(check - PROPERTIES DEPENDS RegressionTest_${TestFile_Name}) - endif() - endforeach() -endif() +add_ledger_harness_tests(Regress) diff --git a/test/regress/xact_code.dat b/test/regress/xact_code.dat new file mode 100644 index 00000000..60956a23 --- /dev/null +++ b/test/regress/xact_code.dat @@ -0,0 +1,3 @@ +2012-11-10 (C0-d3) Payee + Assets:Checking € -12,45 + Expenses:Expenditure diff --git a/test/regress/xact_code.py b/test/regress/xact_code.py new file mode 100644 index 00000000..64abb17d --- /dev/null +++ b/test/regress/xact_code.py @@ -0,0 +1,4 @@ +import ledger + +for post in ledger.read_journal('test/regress/xact_code.dat').query('expenses'): + print post.xact.code diff --git a/test/regress/xact_code_py.test b/test/regress/xact_code_py.test new file mode 100644 index 00000000..c22158e0 --- /dev/null +++ b/test/regress/xact_code_py.test @@ -0,0 +1,3 @@ +test python test/regress/xact_code.py +C0-d3 +end test diff --git a/test/unit/t_amount.cc b/test/unit/t_amount.cc index 07cde8f3..b82de510 100644 --- a/test/unit/t_amount.cc +++ b/test/unit/t_amount.cc @@ -1109,6 +1109,60 @@ BOOST_AUTO_TEST_CASE(testCommodityAbs) BOOST_CHECK(x2.valid()); } +BOOST_AUTO_TEST_CASE(testFloor) +{ + amount_t x0; + amount_t x1("123.123"); + amount_t x2("-123.123"); + + BOOST_CHECK_THROW(x0.floored(), amount_error); + BOOST_CHECK_EQUAL(amount_t(123L), x1.floored()); + BOOST_CHECK_EQUAL(amount_t(-124L), x2.floored()); + + BOOST_CHECK(x0.valid()); + BOOST_CHECK(x1.valid()); + BOOST_CHECK(x2.valid()); +} + +BOOST_AUTO_TEST_CASE(testCommodityFloor) +{ + amount_t x1("$1234.56"); + amount_t x2("$-1234.56"); + + BOOST_CHECK_EQUAL(amount_t("$1234"), x1.floored()); + BOOST_CHECK_EQUAL(amount_t("$-1235"), x2.floored()); + + BOOST_CHECK(x1.valid()); + BOOST_CHECK(x2.valid()); +} + +BOOST_AUTO_TEST_CASE(testCeiling) +{ + amount_t x0; + amount_t x1("123.123"); + amount_t x2("-123.123"); + + BOOST_CHECK_THROW(x0.ceilinged(), amount_error); + BOOST_CHECK_EQUAL(amount_t(124L), x1.ceilinged()); + BOOST_CHECK_EQUAL(amount_t(-123L), x2.ceilinged()); + + BOOST_CHECK(x0.valid()); + BOOST_CHECK(x1.valid()); + BOOST_CHECK(x2.valid()); +} + +BOOST_AUTO_TEST_CASE(testCommodityCeiling) +{ + amount_t x1("$1234.56"); + amount_t x2("$-1234.56"); + + BOOST_CHECK_EQUAL(amount_t("$1235"), x1.ceilinged()); + BOOST_CHECK_EQUAL(amount_t("$-1234"), x2.ceilinged()); + + BOOST_CHECK(x1.valid()); + BOOST_CHECK(x2.valid()); +} + #ifndef NOT_FOR_PYTHON #if 0 BOOST_AUTO_TEST_CASE(testReduction) diff --git a/tools/build.sh b/tools/build.sh index a37b06f4..e79689e3 100755 --- a/tools/build.sh +++ b/tools/build.sh @@ -4,12 +4,12 @@ flavor=$1 shift 1 JOBS=-j$(sysctl -n hw.activecpu) -OPTIONS="$flavor --debug --python --doxygen $JOBS" +OPTIONS="$flavor --debug --python --ninja --doxygen $JOBS" -time ( \ - cd ~/src/ledger ; \ - PATH=/usr/local/bin:/opt/local/bin:$PATH \ - nice -n 20 ./acprep $OPTIONS make "$@" && \ - PATH=/usr/local/bin:/opt/local/bin:$PATH \ - nice -n 20 ./acprep $OPTIONS check "$@" \ +time ( \ + cd ~/src/ledger ; \ + PATH=/usr/local/bin:/opt/local/bin:$PATH \ + nice -n 20 ./acprep $OPTIONS make $JOBS "$@" && \ + PATH=/usr/local/bin:/opt/local/bin:$PATH \ + nice -n 20 ./acprep $OPTIONS check $JOBS "$@" \ ) diff --git a/tools/gendocs.sh b/tools/gendocs.sh index 55933b13..9126a406 100755 --- a/tools/gendocs.sh +++ b/tools/gendocs.sh @@ -2,4 +2,4 @@ echo "===================================== Making Info..." makeinfo ledger3.texi echo "===================================== Making PDF..." -texi2pdf ledger3.texi +texi2pdf --quiet --batch ledger3.texi |