diff options
105 files changed, 4950 insertions, 1387 deletions
diff --git a/Makefile.am b/Makefile.am index cdc7d4b3..8ed18fbb 100644 --- a/Makefile.am +++ b/Makefile.am @@ -30,6 +30,9 @@ libledger_util_la_LDFLAGS = -release $(VERSION).0 libledger_math_la_SOURCES = \ src/value.cc \ src/balance.cc \ + src/quotes.cc \ + src/pool.cc \ + src/annotate.cc \ src/commodity.cc \ src/amount.cc @@ -65,7 +68,6 @@ libledger_data_la_CPPFLAGS = $(lib_cppflags) libledger_data_la_LDFLAGS = -release $(VERSION).0 libledger_report_la_SOURCES = \ - src/quotes.cc \ src/stats.cc \ src/generate.cc \ src/derive.cc \ @@ -94,6 +96,9 @@ pkginclude_HEADERS = \ \ src/amount.h \ src/commodity.h \ + src/annotate.h \ + src/pool.h \ + src/quotes.h \ src/balance.h \ src/value.h \ \ @@ -126,12 +131,9 @@ pkginclude_HEADERS = \ src/stats.h \ src/output.h \ src/emacs.h \ - src/quotes.h \ \ src/global.h \ - src/ledger.h \ \ - src/pyledger.h \ src/pyinterp.h \ \ lib/fdstream.h \ @@ -584,7 +584,7 @@ class PrepareBuild(CommandLineApp): environ, conf_args = self.configure_environment() - boost = 'boost_1_38_0' + boost = 'boost_1_39_0' tarball = boost + '.tar.bz2' if not exists(boost): @@ -604,7 +604,7 @@ class PrepareBuild(CommandLineApp): fd.close() digest = csum.hexdigest() - if digest != '5eca2116d39d61382b8f8235915cb267': + if digest != 'a17281fd88c48e0d866e1a12deecbcc0': self.log.error('Boost source tarball fails to match checksum') sys.exit(1) @@ -950,17 +950,17 @@ class PrepareBuild(CommandLineApp): self.sys_include_dirs.insert(0, '/usr/local/stow/cppunit-debug/include') self.sys_library_dirs.insert(0, '/usr/local/stow/cppunit-debug/lib') - if exists('/usr/local/lib/libboost_regex-xgcc43-sd-1_38.a'): + if exists('/usr/local/lib/libboost_regex-xgcc43-sd-1_39.a'): self.envvars['BOOST_HOME'] = '/usr/local' - self.envvars['BOOST_SUFFIX'] = '-xgcc43-sd-1_38' - self.envvars['BOOST_VERSION'] = '1_38' + self.envvars['BOOST_SUFFIX'] = '-xgcc43-sd-1_39' + self.envvars['BOOST_VERSION'] = '1_39' self.log.debug('Setting BOOST_SUFFIX => %s' % self.envvars['BOOST_SUFFIX']) self.log.debug('Setting BOOST_VERSION => %s' % self.envvars['BOOST_VERSION']) - self.sys_include_dirs.append('/usr/local/include/boost-1_38') + self.sys_include_dirs.append('/usr/local/include/boost-1_39') def setup_flavor_opt(self): self.CXXFLAGS.append('-O3') @@ -996,6 +996,7 @@ class PrepareBuild(CommandLineApp): line = re.sub('^\t(\$\((LIBTOOL|CXX)\).*?\.(cc|cpp))$', '\t@echo " " CXX \$@;\\1 > /dev/null', line) line = re.sub('^\tmv -f', '\t@mv -f', line) + line = re.sub('^\t\$\(am__mv\)', '\t@$(am__mv)', line) line = re.sub('^\t(\$\((.*?)LINK\).*)', '\t@echo " " LD \$@;\\1 > /dev/null', line) Makefile_new.write(line) diff --git a/contrib/getquote.pl b/contrib/getquote.pl index bed561d6..8e3bb678 100755 --- a/contrib/getquote.pl +++ b/contrib/getquote.pl @@ -1,8 +1,9 @@ -#!/usr/bin/perl +#!/usr/bin/env perl $timeout = 60; use Finance::Quote; +use POSIX qw(strftime localtime time); $q = Finance::Quote->new; $q->timeout($timeout); @@ -10,6 +11,8 @@ $q->require_labels(qw/price/); %quotes = $q->fetch("nasdaq", $ARGV[0]); if ($quotes{$ARGV[0], "price"}) { + print strftime('%Y/%m/%d %H:%M:%S', localtime(time())); + print " ", $ARGV[0], " "; print "\$", $quotes{$ARGV[0], "price"}, "\n"; } else { exit 1; diff --git a/lib/Makefile b/lib/Makefile index 67b45485..38ff924d 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -8,7 +8,7 @@ STOW_ROOT = /usr/local/stow BOOST_SOURCE = boost -BOOST_VERSION = 1_38_0 +BOOST_VERSION = 1_39_0 # architecture=combined boost-release: diff --git a/lisp/ledger.el b/lisp/ledger.el index a649250d..be730fd9 100644 --- a/lisp/ledger.el +++ b/lisp/ledger.el @@ -128,17 +128,17 @@ text that should replace the format specifier." (defvar bold 'bold) (defvar ledger-font-lock-keywords - '(("^[0-9./=]+\\s-+\\(?:([^)]+)\\s-+\\)?\\([^*].+\\)" 1 bold) - ("\s \\{5,\\}\\([$]-[0-9][0-9,.]*\\)" 1 font-lock-warning-face) - ("\s \\{5,\\}\\(-?[0-9][0-9,.]*\\)" 1 font-lock-type-face) - ("^\\s-+.+?\\( \\|\t\\|\n\\|\\s-+$\\)" . font-lock-keyword-face) - ("^\s +\\((\\)\\([A-Za-z0-9]+:[A-Za-z0-9]+[A-Za-z0-9: ]*\\)\\()\\)" - (1 font-lock-function-name-face) - (2 font-lock-variable-name-face) - (3 font-lock-function-name-face)) - ("^[0-9]+[/-][0-9]+.*\\([*]\\)" 1 bold) - ("^\\([~=]\\)\\s " 1 font-lock-function-name-face)) - "Improved expressions to highlight in Ledger mode.") + '(("\\( \\| \\|^\\)\\(;.*\\)" 2 font-lock-comment-face) + ("^[0-9]+[-/.=][-/.=0-9]+\\s-+\\(([^)]+)\\s-+\\)?\\([^*].+?\\)\\(\\( ;\\| ;\\|$\\)\\)" 2 bold) + ;;("^[0-9]+[-/.=][-/.=0-9]+\\s-+\\(([^)]+)\\s-+\\)?\\([*].+?\\)\\(\\( ;\\| ;\\|$\\)\\)" + ;; 2 font-lock-type-face) + ("^\\s-+\\([*]\\s-*\\)?\\(\\([[(]\\)?[^*: + ]+?:[^]); + ]+?\\([])]\\)?\\)\\( \\| \\|$\\)" + 2 font-lock-keyword-face) + ("^\\([~=].+\\)" 1 font-lock-function-name-face) + ("^\\([A-Za-z]+ .+\\)" 1 font-lock-function-name-face)) + "Expressions to highlight in Ledger mode.") (defsubst ledger-current-year () (format-time-string "%Y")) @@ -225,9 +225,7 @@ Return the difference in the format of a time value." (mapcar 'eval args))) (goto-char (point-min)) (if (looking-at "Error: ") - (progn - (message (buffer-string)) - (error)) + (error (buffer-string)) (buffer-string))) "\n")))) @@ -415,8 +413,20 @@ dropped." (defun ledger-toggle-current (&optional style) (interactive) - (if ledger-clear-whole-entries - (ledger-toggle-current-entry style) + (if (or ledger-clear-whole-entries + (eq 'entry (ledger-thing-at-point))) + (progn + (save-excursion + (forward-line) + (goto-char (line-beginning-position)) + (while (and (not (eolp)) + (save-excursion + (not (eq 'entry (ledger-thing-at-point))))) + (if (looking-at "\\s-+[*!]") + (ledger-toggle-current-transaction nil)) + (forward-line) + (goto-char (line-beginning-position)))) + (ledger-toggle-current-entry style)) (ledger-toggle-current-transaction style))) (defvar ledger-mode-abbrev-table) @@ -483,7 +493,7 @@ dropped." (account ledger-acct) (inhibit-read-only t) cleared) - (when (equal (car where) "<stdin>") + (when (or (equal (car where) "<stdin>") (equal (car where) "/dev/stdin")) (with-current-buffer ledger-buf (goto-char (cdr where)) (setq cleared (ledger-toggle-current 'pending))) @@ -522,7 +532,7 @@ dropped." (defun ledger-reconcile-delete () (interactive) (let ((where (get-text-property (point) 'where))) - (when (equal (car where) "<stdin>") + (when (or (equal (car where) "<stdin>") (equal (car where) "/dev/stdin")) (with-current-buffer ledger-buf (goto-char (cdr where)) (ledger-delete-current-entry)) @@ -534,7 +544,7 @@ dropped." (defun ledger-reconcile-visit () (interactive) (let ((where (get-text-property (point) 'where))) - (when (equal (car where) "<stdin>") + (when (or (equal (car where) "<stdin>") (equal (car where) "/dev/stdin")) (switch-to-buffer-other-window ledger-buf) (goto-char (cdr where))))) @@ -557,7 +567,7 @@ dropped." (let ((where (get-text-property (point) 'where)) (face (get-text-property (point) 'face))) (if (and (eq face 'bold) - (equal (car where) "<stdin>")) + (or (equal (car where) "<stdin>") (equal (car where) "/dev/stdin"))) (with-current-buffer ledger-buf (goto-char (cdr where)) (ledger-toggle-current 'cleared)))) diff --git a/src/account.cc b/src/account.cc index 711e815e..c2628134 100644 --- a/src/account.cc +++ b/src/account.cc @@ -140,7 +140,7 @@ string account_t::partial_name(bool flat) const if (! flat) { std::size_t count = acct->children_with_flags(ACCOUNT_EXT_TO_DISPLAY); assert(count > 0); - if (count > 1) + if (count > 1 || acct->has_flags(ACCOUNT_EXT_TO_DISPLAY)) break; } pname = acct->name + ":" + pname; @@ -194,6 +194,10 @@ namespace { return false; } + value_t get_true(account_t&) { + return true; + } + value_t get_depth_spacer(account_t& account) { std::size_t depth = 0; @@ -202,7 +206,7 @@ namespace { acct = acct->parent) { std::size_t count = acct->children_with_flags(ACCOUNT_EXT_TO_DISPLAY); assert(count > 0); - if (count > 1) + if (count > 1 || acct->has_flags(ACCOUNT_EXT_TO_DISPLAY)) depth++; } @@ -217,6 +221,10 @@ namespace { value_t get_wrapper(call_scope_t& scope) { return (*Func)(find_scope<account_t>(scope)); } + + value_t get_parent(account_t& account) { + return value_t(static_cast<scope_t *>(account.parent)); + } } expr_t::ptr_op_t account_t::lookup(const string& name) @@ -243,9 +251,16 @@ expr_t::ptr_op_t account_t::lookup(const string& name) return WRAP_FUNCTOR(get_wrapper<&get_depth_spacer>); break; + case 'i': + if (name == "is_account") + return WRAP_FUNCTOR(get_wrapper<&get_true>); + break; + case 'p': if (name == "partial_account") return WRAP_FUNCTOR(get_partial_name); + else if (name == "parent") + return WRAP_FUNCTOR(get_wrapper<&get_parent>); break; case 's': diff --git a/src/account.h b/src/account.h index 2f3eec24..56c0c72b 100644 --- a/src/account.h +++ b/src/account.h @@ -74,7 +74,6 @@ class account_t : public scope_t posts_deque posts; bool known; - mutable void * data; mutable string _fullname; account_t(account_t * _parent = NULL, @@ -82,7 +81,7 @@ class account_t : public scope_t const optional<string>& _note = none) : scope_t(), parent(_parent), name(_name), note(_note), depth(static_cast<unsigned short>(parent ? parent->depth + 1 : 0)), - known(false), data(NULL) { + known(false) { TRACE_CTOR(account_t, "account_t *, const string&, const string&"); } account_t(const account_t& other) @@ -92,10 +91,8 @@ class account_t : public scope_t note(other.note), depth(other.depth), accounts(other.accounts), - known(other.known), - data(NULL) { + known(other.known) { TRACE_CTOR(account_t, "copy"); - assert(other.data == NULL); } ~account_t(); diff --git a/src/amount.cc b/src/amount.cc index 7c9a6931..2434f110 100644 --- a/src/amount.cc +++ b/src/amount.cc @@ -33,6 +33,8 @@ #include "amount.h" #include "commodity.h" +#include "annotate.h" +#include "pool.h" namespace ledger { @@ -343,8 +345,8 @@ amount_t& amount_t::operator*=(const amount_t& amt) _dup(); mpq_mul(MP(quantity), MP(quantity), MP(amt.quantity)); - quantity->prec = static_cast<precision_t>(quantity->prec + - amt.quantity->prec); + quantity->prec = + static_cast<precision_t>(quantity->prec + amt.quantity->prec); if (! has_commodity()) commodity_ = amt.commodity_; @@ -382,7 +384,7 @@ amount_t& amount_t::operator/=(const amount_t& amt) mpq_div(MP(quantity), MP(quantity), MP(amt.quantity)); quantity->prec = static_cast<precision_t>(quantity->prec + amt.quantity->prec + - quantity->prec + extend_by_digits); + extend_by_digits); if (! has_commodity()) commodity_ = amt.commodity_; @@ -488,7 +490,10 @@ void amount_t::in_place_unround() return; _dup(); + + DEBUG("amount.unround", "Unrounding " << *this); set_keep_precision(true); + DEBUG("amount.unround", "Unrounded = " << *this); } void amount_t::in_place_reduce() @@ -551,17 +556,37 @@ amount_t::value(const bool primary_only, annotation().has_flags(ANNOTATION_PRICE_FIXATED)) { return (*annotation().price * number()).rounded(); } - else if (optional<price_point_t> point = - commodity().find_price(in_terms_of, moment)) { - return (point->price * number()).rounded(); + else { + optional<price_point_t> point = + commodity().find_price(in_terms_of, moment); + + // Whether a price was found or not, check whether we should attempt + // to download a price from the Internet. This is done if (a) no + // price was found, or (b) the price is "stale" according to the + // setting of --price-exp. + point = commodity().check_for_updated_price(point, moment, in_terms_of); + if (point) + return (point->price * number()).rounded(); } } } else { - throw_(amount_error, _("Cannot determine value of an uninitialized amount")); + throw_(amount_error, + _("Cannot determine value of an uninitialized amount")); } return none; } +amount_t amount_t::price() const +{ + if (is_annotated() && annotation().price) { + amount_t temp(*annotation().price); + temp *= *this; + DEBUG("amount.price", "Returning price of " << *this << " = " << temp); + return temp; + } + return *this; +} + int amount_t::sign() const { @@ -740,7 +765,7 @@ void amount_t::annotate(const annotation_t& details) if (! quantity) throw_(amount_error, _("Cannot annotate the commodity of an uninitialized amount")); else if (! has_commodity()) - throw_(amount_error, _("Cannot annotate an amount with no commodity")); + return; // ignore attempt to annotate a "bare commodity if (commodity().annotated) { this_ann = &as_annotated_commodity(commodity()); @@ -770,8 +795,9 @@ bool amount_t::is_annotated() const throw_(amount_error, _("Cannot determine if an uninitialized amount's commodity is annotated")); - assert(! commodity().annotated || as_annotated_commodity(commodity()).details); - return commodity().annotated; + assert(! has_commodity() || ! commodity().annotated || + as_annotated_commodity(commodity()).details); + return has_commodity() && commodity().annotated; } annotation_t& amount_t::annotation() @@ -1062,8 +1088,10 @@ void amount_t::print(std::ostream& _out) const bool amount_t::valid() const { if (quantity) { - if (! quantity->valid()) + if (! quantity->valid()) { + DEBUG("ledger.validate", "amount_t: ! quantity->valid()"); return false; + } if (quantity->ref == 0) { DEBUG("ledger.validate", "amount_t: quantity->ref == 0"); diff --git a/src/amount.h b/src/amount.h index 990b326f..b3c632af 100644 --- a/src/amount.h +++ b/src/amount.h @@ -384,6 +384,8 @@ public: const optional<datetime_t>& moment = none, const optional<commodity_t&>& in_terms_of = none) const; + amount_t price() const; + /*@}*/ /** @name Truth tests diff --git a/src/annotate.cc b/src/annotate.cc new file mode 100644 index 00000000..1ea39b5d --- /dev/null +++ b/src/annotate.cc @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of New Artisans LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <system.hh> + +#include "amount.h" +#include "commodity.h" +#include "annotate.h" +#include "pool.h" + +namespace ledger { + +void annotation_t::parse(std::istream& in) +{ + do { + istream_pos_type pos = in.tellg(); + + char buf[256]; + char c = peek_next_nonws(in); + if (c == '{') { + if (price) + throw_(amount_error, _("Commodity specifies more than one price")); + + in.get(c); + c = peek_next_nonws(in); + if (c == '=') { + in.get(c); + add_flags(ANNOTATION_PRICE_FIXATED); + } + + READ_INTO(in, buf, 255, c, c != '}'); + if (c == '}') + in.get(c); + else + throw_(amount_error, _("Commodity price lacks closing brace")); + + amount_t temp; + temp.parse(buf, amount_t::PARSE_NO_MIGRATE); + + DEBUG("commodity.annotations", "Parsed annotation price: " << temp); + + // Since this price will maintain its own precision, make sure + // it is at least as large as the base commodity, since the user + // may have only specified {$1} or something similar. + + if (temp.has_commodity() && + temp.precision() > temp.commodity().precision()) + temp = temp.rounded(); // no need to retain individual precision + + price = temp; + } + else if (c == '[') { + if (date) + throw_(amount_error, _("Commodity specifies more than one date")); + + in.get(c); + READ_INTO(in, buf, 255, c, c != ']'); + if (c == ']') + in.get(c); + else + throw_(amount_error, _("Commodity date lacks closing bracket")); + + date = parse_date(buf); + } + else if (c == '(') { + if (tag) + throw_(amount_error, _("Commodity specifies more than one tag")); + + in.get(c); + READ_INTO(in, buf, 255, c, c != ')'); + if (c == ')') + in.get(c); + else + throw_(amount_error, _("Commodity tag lacks closing parenthesis")); + + tag = buf; + } + else { + in.clear(); + in.seekg(pos, std::ios::beg); + break; + } + } while (true); + +#if defined(DEBUG_ON) + if (SHOW_DEBUG("amounts.commodities") && *this) { + DEBUG("amounts.commodities", + "Parsed commodity annotations: " << std::endl << *this); + } +#endif +} + +void annotation_t::print(std::ostream& out, bool keep_base) const +{ + if (price) + out << " {" + << (has_flags(ANNOTATION_PRICE_FIXATED) ? "=" : "") + << (keep_base ? *price : price->unreduced()).rounded() + << '}'; + + if (date) + out << " [" << format_date(*date, string("%Y/%m/%d")) << ']'; + + if (tag) + out << " (" << *tag << ')'; +} + +bool keep_details_t::keep_all(const commodity_t& comm) const +{ + return (! comm.annotated || + (keep_price && keep_date && keep_tag && ! only_actuals)); +} + +bool keep_details_t::keep_any(const commodity_t& comm) const +{ + return comm.annotated && (keep_price || keep_date || keep_tag); +} + +bool annotated_commodity_t::operator==(const commodity_t& comm) const +{ + // If the base commodities don't match, the game's up. + if (base != comm.base) + return false; + + assert(annotated); + if (! comm.annotated) + return false; + + if (details != as_annotated_commodity(comm).details) + return false; + + return true; +} + +commodity_t& +annotated_commodity_t::strip_annotations(const keep_details_t& what_to_keep) +{ + DEBUG("commodity.annotated.strip", + "Reducing commodity " << *this << std::endl + << " keep price " << what_to_keep.keep_price << " " + << " keep date " << what_to_keep.keep_date << " " + << " keep tag " << what_to_keep.keep_tag); + + commodity_t * new_comm; + + bool keep_price = (what_to_keep.keep_price && + (! what_to_keep.only_actuals || + ! details.has_flags(ANNOTATION_PRICE_CALCULATED))); + bool keep_date = (what_to_keep.keep_date && + (! what_to_keep.only_actuals || + ! details.has_flags(ANNOTATION_DATE_CALCULATED))); + bool keep_tag = (what_to_keep.keep_tag && + (! what_to_keep.only_actuals || + ! details.has_flags(ANNOTATION_TAG_CALCULATED))); + + if ((keep_price && details.price) || + (keep_date && details.date) || + (keep_tag && details.tag)) + { + new_comm = parent().find_or_create + (referent(), annotation_t(keep_price ? details.price : none, + keep_date ? details.date : none, + keep_tag ? details.tag : none)); + } else { + new_comm = parent().find_or_create(base_symbol()); + } + + assert(new_comm); + return *new_comm; +} + +void annotated_commodity_t::write_annotations(std::ostream& out) const +{ + details.print(out, parent().keep_base); +} + +} // namespace ledger diff --git a/src/annotate.h b/src/annotate.h new file mode 100644 index 00000000..d98f7ef6 --- /dev/null +++ b/src/annotate.h @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of New Artisans LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @addtogroup math + */ + +/** + * @file annotate.h + * @author John Wiegley + * + * @ingroup math + * + * @brief Types for annotating commodities + * + * Long. + */ +#ifndef _ANNOTATE_H +#define _ANNOTATE_H + +namespace ledger { + +/** + * @brief Brief + * + * Long. + */ +struct annotation_t : public supports_flags<>, + public equality_comparable<annotation_t> +{ +#define ANNOTATION_PRICE_CALCULATED 0x01 +#define ANNOTATION_PRICE_FIXATED 0x02 +#define ANNOTATION_DATE_CALCULATED 0x04 +#define ANNOTATION_TAG_CALCULATED 0x08 + + optional<amount_t> price; + optional<date_t> date; + optional<string> tag; + + explicit annotation_t(const optional<amount_t>& _price = none, + const optional<date_t>& _date = none, + const optional<string>& _tag = none) + : supports_flags<>(), price(_price), date(_date), tag(_tag) { + TRACE_CTOR(annotation_t, "const optional<amount_t>& + date_t + string"); + } + annotation_t(const annotation_t& other) + : supports_flags<>(other.flags()), + price(other.price), date(other.date), tag(other.tag) { + TRACE_CTOR(annotation_t, "copy"); + } + ~annotation_t() { + TRACE_DTOR(annotation_t); + } + + operator bool() const { + return price || date || tag; + } + + bool operator==(const annotation_t& rhs) const { + return (price == rhs.price && + date == rhs.date && + tag == rhs.tag); + } + + void parse(std::istream& in); + + void print(std::ostream& out, bool keep_base = false) const; + + bool valid() const { + assert(*this); + return true; + } +}; + +struct keep_details_t +{ + bool keep_price; + bool keep_date; + bool keep_tag; + bool only_actuals; + + explicit keep_details_t(bool _keep_price = false, + bool _keep_date = false, + bool _keep_tag = false, + bool _only_actuals = false) + : keep_price(_keep_price), + keep_date(_keep_date), + keep_tag(_keep_tag), + only_actuals(_only_actuals) + { + TRACE_CTOR(keep_details_t, "bool, bool, bool, bool"); + } + keep_details_t(const keep_details_t& other) + : keep_price(other.keep_price), keep_date(other.keep_date), + keep_tag(other.keep_tag), only_actuals(other.only_actuals) { + TRACE_CTOR(keep_details_t, "copy"); + } + ~keep_details_t() throw() { + TRACE_DTOR(keep_details_t); + } + + bool keep_all() const { + return keep_price && keep_date && keep_tag && ! only_actuals; + } + bool keep_all(const commodity_t& comm) const; + + bool keep_any() const { + return keep_price || keep_date || keep_tag; + } + bool keep_any(const commodity_t& comm) const; +}; + +inline std::ostream& operator<<(std::ostream& out, + const annotation_t& details) { + details.print(out); + return out; +} + +/** + * @brief Brief + * + * Long. + */ +class annotated_commodity_t + : public commodity_t, + public equality_comparable<annotated_commodity_t, + equality_comparable2<annotated_commodity_t, commodity_t, + noncopyable> > +{ +public: + commodity_t * ptr; + annotation_t details; + + explicit annotated_commodity_t(commodity_t * _ptr, + const annotation_t& _details) + : commodity_t(_ptr->parent_, _ptr->base), ptr(_ptr), details(_details) { + TRACE_CTOR(annotated_commodity_t, ""); + annotated = true; + } + virtual ~annotated_commodity_t() { + TRACE_DTOR(annotated_commodity_t); + } + + virtual bool operator==(const commodity_t& comm) const; + virtual bool operator==(const annotated_commodity_t& comm) const { + return *this == static_cast<const commodity_t&>(comm); + } + + virtual commodity_t& referent() { + return *ptr; + } + virtual const commodity_t& referent() const { + return *ptr; + } + + virtual commodity_t& strip_annotations(const keep_details_t& what_to_keep); + virtual void write_annotations(std::ostream& out) const; +}; + +inline annotated_commodity_t& +as_annotated_commodity(commodity_t& commodity) { + return downcast<annotated_commodity_t>(commodity); +} +inline const annotated_commodity_t& +as_annotated_commodity(const commodity_t& commodity) { + return downcast<const annotated_commodity_t>(commodity); +} + +} // namespace ledger + +#endif // _ANNOTATE_H diff --git a/src/balance.cc b/src/balance.cc index 864926cf..274f860a 100644 --- a/src/balance.cc +++ b/src/balance.cc @@ -33,6 +33,7 @@ #include "balance.h" #include "commodity.h" +#include "pool.h" #include "unistring.h" // for justify() namespace ledger { @@ -202,6 +203,16 @@ balance_t::value(const bool primary_only, return resolved ? temp : optional<balance_t>(); } +balance_t balance_t::price() const +{ + balance_t temp; + + foreach (const amounts_map::value_type& pair, amounts) + temp += pair.second.price(); + + return temp; +} + optional<amount_t> balance_t::commodity_amount(const optional<const commodity_t&>& commodity) const { @@ -243,7 +254,8 @@ balance_t::strip_annotations(const keep_details_t& what_to_keep) const void balance_t::print(std::ostream& out, const int first_width, const int latter_width, - const bool right_justify) const + const bool right_justify, + const bool colorize) const { bool first = true; int lwidth = latter_width; @@ -272,7 +284,8 @@ void balance_t::print(std::ostream& out, std::ostringstream buf; buf << *amount; - justify(out, buf.str(), width, right_justify); + justify(out, buf.str(), width, right_justify, + colorize && amount->sign() < 0); } if (first) { diff --git a/src/balance.h b/src/balance.h index 15657c5e..765c46dd 100644 --- a/src/balance.h +++ b/src/balance.h @@ -367,6 +367,8 @@ public: const optional<datetime_t>& moment = none, const optional<commodity_t&>& in_terms_of = none) const; + balance_t price() const; + /** * Truth tests. An balance may be truth test in two ways: * @@ -483,7 +485,8 @@ public: void print(std::ostream& out, const int first_width = -1, const int latter_width = -1, - const bool right_justify = true) const; + const bool right_justify = true, + const bool colorize = true) const; /** * Debugging methods. There are two methods defined to help with @@ -511,8 +514,10 @@ public: bool valid() const { foreach (const amounts_map::value_type& pair, amounts) - if (! pair.second.valid()) + if (! pair.second.valid()) { + DEBUG("ledger.validate", "balance_t: ! pair.second.valid()"); return false; + } return true; } }; diff --git a/src/commodity.cc b/src/commodity.cc index 67a86b87..37f0b573 100644 --- a/src/commodity.cc +++ b/src/commodity.cc @@ -33,6 +33,8 @@ #include "amount.h" #include "commodity.h" +#include "annotate.h" +#include "pool.h" namespace ledger { @@ -95,8 +97,9 @@ void commodity_t::base_t::varied_history_t:: hist->add_price(source, date, price, reflexive); } -bool commodity_t::base_t::varied_history_t::remove_price(const datetime_t& date, - commodity_t& comm) +bool commodity_t::base_t::varied_history_t:: + remove_price(const datetime_t& date, + commodity_t& comm) { DEBUG("commodity.prices.add", "varied_remove_price: " << date << ", " << comm); @@ -107,8 +110,8 @@ bool commodity_t::base_t::varied_history_t::remove_price(const datetime_t& date optional<price_point_t> commodity_t::base_t::history_t:: - find_price(const optional<datetime_t>& moment, - const optional<datetime_t>& oldest + find_price(const optional<datetime_t>& moment, + const optional<datetime_t>& oldest #if defined(DEBUG_ON) , const int indent #endif @@ -185,16 +188,6 @@ optional<price_point_t> } } -#if 0 - if (! has_flags(COMMODITY_NOMARKET) && parent().get_quote) { - if (optional<amount_t> quote = parent().get_quote - (*this, age, moment, - (hist && hist->prices.size() > 0 ? - (*hist->prices.rbegin()).first : optional<datetime_t>()))) - return *quote; - } -#endif - if (! found) { #if defined(DEBUG_ON) DEBUG_INDENT("commodity.prices.find", indent); @@ -351,6 +344,8 @@ optional<price_point_t> DEBUG_INDENT("commodity.prices.find", indent); DEBUG("commodity.prices.find", " found price " << best.price << " from " << best.when); + DEBUG("commodity.download", + "found price " << best.price << " from " << best.when); #endif return best; } @@ -368,7 +363,8 @@ optional<commodity_t::base_t::history_t&> #if 0 // jww (2008-09-20): Document which option switch to use here throw_(commodity_error, - _("Cannot determine price history: prices known for multiple commodities (use -x)")); + _("Cannot determine price history: " + "prices known for multiple commodities (use -x)")); #endif comm = (*histories.begin()).first; } else { @@ -382,78 +378,43 @@ optional<commodity_t::base_t::history_t&> return none; } -void commodity_t::exchange(commodity_t& commodity, - const amount_t& per_unit_cost, - const datetime_t& moment) -{ - DEBUG("commodity.prices.add", "exchanging commodity " << commodity - << " at per unit cost " << per_unit_cost << " on " << moment); - - commodity_t& base_commodity - (commodity.annotated ? - as_annotated_commodity(commodity).referent() : commodity); - - base_commodity.add_price(moment, per_unit_cost); -} - -commodity_t::cost_breakdown_t -commodity_t::exchange(const amount_t& amount, - const amount_t& cost, - const bool is_per_unit, - const optional<datetime_t>& moment, - const optional<string>& tag) +optional<price_point_t> +commodity_t::check_for_updated_price(const optional<price_point_t>& point, + const optional<datetime_t>& moment, + const optional<commodity_t&>& in_terms_of) { - DEBUG("commodity.prices.add", "exchange: " << amount << " for " << cost); - DEBUG("commodity.prices.add", "exchange: is-per-unit = " << is_per_unit); -#if defined(DEBUG_ON) - if (moment) - DEBUG("commodity.prices.add", "exchange: moment = " << *moment); - if (tag) - DEBUG("commodity.prices.add", "exchange: tag = " << *tag); -#endif - - commodity_t& commodity(amount.commodity()); - - annotation_t * current_annotation = NULL; - if (commodity.annotated) - current_annotation = &as_annotated_commodity(commodity).details; - - amount_t per_unit_cost = (is_per_unit ? cost : cost / amount).abs(); - - DEBUG("commodity.prices.add", "exchange: per-unit-cost = " << per_unit_cost); - - exchange(commodity, per_unit_cost, moment ? *moment : CURRENT_TIME()); - - cost_breakdown_t breakdown; - breakdown.final_cost = ! is_per_unit ? cost : cost * amount; + if (parent().get_quotes && ! has_flags(COMMODITY_NOMARKET)) { + bool exceeds_leeway = true; - DEBUG("commodity.prices.add", - "exchange: final-cost = " << breakdown.final_cost); - - if (current_annotation && current_annotation->price) - breakdown.basis_cost - = (*current_annotation->price * amount).unrounded(); - else - breakdown.basis_cost = breakdown.final_cost; - - DEBUG("commodity.prices.add", - "exchange: basis-cost = " << breakdown.basis_cost); - - annotation_t annotation(per_unit_cost, moment ? - moment->date() : optional<date_t>(), tag); - - annotation.add_flags(ANNOTATION_PRICE_CALCULATED); - if (moment) - annotation.add_flags(ANNOTATION_DATE_CALCULATED); - if (tag) - annotation.add_flags(ANNOTATION_TAG_CALCULATED); - - breakdown.amount = amount_t(amount, annotation); + if (point) { + time_duration_t::sec_type seconds_diff; + if (moment) { + seconds_diff = (*moment - point->when).total_seconds(); + DEBUG("commodity.download", "moment = " << *moment); + DEBUG("commodity.download", "slip.moment = " << seconds_diff); + } else { + seconds_diff = (CURRENT_TIME() - point->when).total_seconds(); + DEBUG("commodity.download", "slip.now = " << seconds_diff); + } - DEBUG("commodity.prices.add", - "exchange: amount = " << breakdown.amount); + DEBUG("commodity.download", "leeway = " << parent().quote_leeway); + if (seconds_diff < parent().quote_leeway) + exceeds_leeway = false; + } - return breakdown; + if (exceeds_leeway) { + DEBUG("commodity.download", + "attempting to download a more current quote..."); + if (optional<price_point_t> quote = + parent().get_commodity_quote(*this, in_terms_of)) { + if (! in_terms_of || + (quote->price.has_commodity() && + quote->price.commodity() == *in_terms_of)) + return quote; + } + } + } + return point; } commodity_t::operator bool() const @@ -499,14 +460,14 @@ void commodity_t::parse_symbol(std::istream& in, string& symbol) { // Invalid commodity characters: // SPACE, TAB, NEWLINE, RETURN - // 0-9 . , ; - + * / ^ ? : & | ! = % + // 0-9 . , ; - + * / ^ ? : & | ! = // < > { } [ ] ( ) @ static int invalid_chars[256] = { /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ /* 00 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 10 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - /* 20 */ 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, + /* 20 */ 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, /* 30 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 */ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 50 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, @@ -635,159 +596,6 @@ bool commodity_t::valid() const return true; } -void annotation_t::parse(std::istream& in) -{ - do { - istream_pos_type pos = in.tellg(); - - char buf[256]; - char c = peek_next_nonws(in); - if (c == '{') { - if (price) - throw_(amount_error, _("Commodity specifies more than one price")); - - in.get(c); - c = peek_next_nonws(in); - if (c == '=') { - in.get(c); - add_flags(ANNOTATION_PRICE_FIXATED); - } - - READ_INTO(in, buf, 255, c, c != '}'); - if (c == '}') - in.get(c); - else - throw_(amount_error, _("Commodity price lacks closing brace")); - - amount_t temp; - temp.parse(buf, amount_t::PARSE_NO_MIGRATE); - - DEBUG("commodity.annotations", "Parsed annotation price: " << temp); - - // Since this price will maintain its own precision, make sure - // it is at least as large as the base commodity, since the user - // may have only specified {$1} or something similar. - - if (temp.has_commodity() && - temp.precision() > temp.commodity().precision()) - temp = temp.rounded(); // no need to retain individual precision - - price = temp; - } - else if (c == '[') { - if (date) - throw_(amount_error, _("Commodity specifies more than one date")); - - in.get(c); - READ_INTO(in, buf, 255, c, c != ']'); - if (c == ']') - in.get(c); - else - throw_(amount_error, _("Commodity date lacks closing bracket")); - - date = parse_date(buf); - } - else if (c == '(') { - if (tag) - throw_(amount_error, _("Commodity specifies more than one tag")); - - in.get(c); - READ_INTO(in, buf, 255, c, c != ')'); - if (c == ')') - in.get(c); - else - throw_(amount_error, _("Commodity tag lacks closing parenthesis")); - - tag = buf; - } - else { - in.clear(); - in.seekg(pos, std::ios::beg); - break; - } - } while (true); - -#if defined(DEBUG_ON) - if (SHOW_DEBUG("amounts.commodities") && *this) { - DEBUG("amounts.commodities", - "Parsed commodity annotations: " << std::endl << *this); - } -#endif -} - -bool annotated_commodity_t::operator==(const commodity_t& comm) const -{ - // If the base commodities don't match, the game's up. - if (base != comm.base) - return false; - - assert(annotated); - if (! comm.annotated) - return false; - - if (details != as_annotated_commodity(comm).details) - return false; - - return true; -} - -commodity_t& -annotated_commodity_t::strip_annotations(const keep_details_t& what_to_keep) -{ - DEBUG("commodity.annotated.strip", - "Reducing commodity " << *this << std::endl - << " keep price " << what_to_keep.keep_price << " " - << " keep date " << what_to_keep.keep_date << " " - << " keep tag " << what_to_keep.keep_tag); - - commodity_t * new_comm; - - bool keep_price = (what_to_keep.keep_price && - (! what_to_keep.only_actuals || - ! details.has_flags(ANNOTATION_PRICE_CALCULATED))); - bool keep_date = (what_to_keep.keep_date && - (! what_to_keep.only_actuals || - ! details.has_flags(ANNOTATION_DATE_CALCULATED))); - bool keep_tag = (what_to_keep.keep_tag && - (! what_to_keep.only_actuals || - ! details.has_flags(ANNOTATION_TAG_CALCULATED))); - - if ((keep_price && details.price) || - (keep_date && details.date) || - (keep_tag && details.tag)) - { - new_comm = parent().find_or_create - (referent(), annotation_t(keep_price ? details.price : none, - keep_date ? details.date : none, - keep_tag ? details.tag : none)); - } else { - new_comm = parent().find_or_create(base_symbol()); - } - - assert(new_comm); - return *new_comm; -} - -void annotated_commodity_t::write_annotations(std::ostream& out) const -{ - details.print(out, parent().keep_base); -} - -void annotation_t::print(std::ostream& out, bool keep_base) const -{ - if (price) - out << " {" - << (has_flags(ANNOTATION_PRICE_FIXATED) ? "=" : "") - << (keep_base ? *price : price->unreduced()).rounded() - << '}'; - - if (date) - out << " [" << format_date(*date, string("%Y/%m/%d")) << ']'; - - if (tag) - out << " (" << *tag << ')'; -} - bool compare_amount_commodities::operator()(const amount_t * left, const amount_t * right) const { @@ -855,190 +663,4 @@ bool compare_amount_commodities::operator()(const amount_t * left, } } -commodity_pool_t::commodity_pool_t() - : default_commodity(NULL), keep_base(false) -{ - TRACE_CTOR(commodity_pool_t, ""); - null_commodity = create(""); - null_commodity->add_flags(COMMODITY_BUILTIN | COMMODITY_NOMARKET); -} - -commodity_t * commodity_pool_t::create(const string& symbol) -{ - shared_ptr<commodity_t::base_t> - base_commodity(new commodity_t::base_t(symbol)); - std::auto_ptr<commodity_t> commodity(new commodity_t(this, base_commodity)); - - DEBUG("amounts.commodities", "Creating base commodity " << symbol); - - // Create the "qualified symbol" version of this commodity's symbol - if (commodity_t::symbol_needs_quotes(symbol)) { - commodity->qualified_symbol = "\""; - *commodity->qualified_symbol += symbol; - *commodity->qualified_symbol += "\""; - } - - DEBUG("amounts.commodities", - "Creating commodity '" << commodity->symbol() << "'"); - - std::pair<commodities_map::iterator, bool> result - = commodities.insert(commodities_map::value_type(commodity->mapping_key(), - commodity.get())); - assert(result.second); - - return commodity.release(); -} - -commodity_t * commodity_pool_t::find_or_create(const string& symbol) -{ - DEBUG("amounts.commodities", "Find-or-create commodity " << symbol); - - commodity_t * commodity = find(symbol); - if (commodity) - return commodity; - return create(symbol); -} - -commodity_t * commodity_pool_t::find(const string& symbol) -{ - DEBUG("amounts.commodities", "Find commodity " << symbol); - - commodities_map::const_iterator i = commodities.find(symbol); - if (i != commodities.end()) - return (*i).second; - return NULL; -} - -commodity_t * -commodity_pool_t::create(const string& symbol, const annotation_t& details) -{ - commodity_t * new_comm = create(symbol); - if (! new_comm) - return NULL; - - if (details) - return find_or_create(*new_comm, details); - else - return new_comm; -} - -namespace { - string make_qualified_name(const commodity_t& comm, - const annotation_t& details) - { - assert(details); - - if (details.price && details.price->sign() < 0) - throw_(amount_error, _("A commodity's price may not be negative")); - - std::ostringstream name; - comm.print(name); - details.print(name, comm.parent().keep_base); - - DEBUG("amounts.commodities", "make_qualified_name for " - << *comm.qualified_symbol << std::endl << details); - DEBUG("amounts.commodities", "qualified_name is " << name.str()); - - return name.str(); - } -} - -commodity_t * -commodity_pool_t::find(const string& symbol, const annotation_t& details) -{ - commodity_t * comm = find(symbol); - if (! comm) - return NULL; - - if (details) { - string name = make_qualified_name(*comm, details); - - if (commodity_t * ann_comm = find(name)) { - assert(ann_comm->annotated && as_annotated_commodity(*ann_comm).details); - return ann_comm; - } - return NULL; - } else { - return comm; - } -} - -commodity_t * -commodity_pool_t::find_or_create(const string& symbol, - const annotation_t& details) -{ - commodity_t * comm = find(symbol); - if (! comm) - return NULL; - - if (details) - return find_or_create(*comm, details); - else - return comm; -} - -commodity_t * -commodity_pool_t::create(commodity_t& comm, - const annotation_t& details, - const string& mapping_key) -{ - assert(comm); - assert(details); - assert(! mapping_key.empty()); - - std::auto_ptr<commodity_t> commodity - (new annotated_commodity_t(&comm, details)); - - commodity->qualified_symbol = comm.symbol(); - assert(! commodity->qualified_symbol->empty()); - - DEBUG("amounts.commodities", "Creating annotated commodity " - << "symbol " << commodity->symbol() - << " key " << mapping_key << std::endl << details); - - // Add the fully annotated name to the map, so that this symbol may - // quickly be found again. - commodity->mapping_key_ = mapping_key; - - std::pair<commodities_map::iterator, bool> result - = commodities.insert(commodities_map::value_type(mapping_key, - commodity.get())); - assert(result.second); - - return commodity.release(); -} - -commodity_t * commodity_pool_t::find_or_create(commodity_t& comm, - const annotation_t& details) -{ - assert(comm); - assert(details); - - string name = make_qualified_name(comm, details); - assert(! name.empty()); - - if (commodity_t * ann_comm = find(name)) { - assert(ann_comm->annotated && as_annotated_commodity(*ann_comm).details); - return ann_comm; - } - return create(comm, details, name); -} - -void commodity_pool_t::parse_commodity_price(char * optarg) -{ - char * equals = std::strchr(optarg, '='); - if (! equals) - return; - - optarg = skip_ws(optarg); - while (equals > optarg && std::isspace(*(equals - 1))) - equals--; - - std::string symbol(optarg, 0, equals - optarg); - amount_t price(equals + 1); - - if (commodity_t * commodity = find_or_create(symbol)) - commodity->add_price(CURRENT_TIME(), price); -} - } // namespace ledger diff --git a/src/commodity.h b/src/commodity.h index 8121199a..5d73f4e8 100644 --- a/src/commodity.h +++ b/src/commodity.h @@ -86,7 +86,6 @@ public: struct history_t { history_map prices; - ptime last_lookup; void add_price(commodity_t& source, const datetime_t& date, @@ -310,24 +309,10 @@ public: return none; } - // Methods to exchange one commodity for another, while recording the - // factored price. - - static void exchange(commodity_t& commodity, - const amount_t& per_unit_cost, - const datetime_t& moment); - - struct cost_breakdown_t { - amount_t amount; - amount_t final_cost; - amount_t basis_cost; - }; - - static cost_breakdown_t exchange(const amount_t& amount, - const amount_t& cost, - const bool is_per_unit = false, - const optional<datetime_t>& moment = none, - const optional<string>& tag = none); + optional<price_point_t> + check_for_updated_price(const optional<price_point_t>& point, + const optional<datetime_t>& moment, + const optional<commodity_t&>& in_terms_of); // Methods related to parsing, reading, writing, etc., the commodity // itself. @@ -357,219 +342,10 @@ inline std::ostream& operator<<(std::ostream& out, const commodity_t& comm) { * * Long. */ -struct annotation_t : public supports_flags<>, - public equality_comparable<annotation_t> -{ -#define ANNOTATION_PRICE_CALCULATED 0x01 -#define ANNOTATION_PRICE_FIXATED 0x02 -#define ANNOTATION_DATE_CALCULATED 0x04 -#define ANNOTATION_TAG_CALCULATED 0x08 - - optional<amount_t> price; - optional<date_t> date; - optional<string> tag; - - explicit annotation_t(const optional<amount_t>& _price = none, - const optional<date_t>& _date = none, - const optional<string>& _tag = none) - : supports_flags<>(), price(_price), date(_date), tag(_tag) { - TRACE_CTOR(annotation_t, "const optional<amount_t>& + date_t + string"); - } - annotation_t(const annotation_t& other) - : supports_flags<>(other.flags()), - price(other.price), date(other.date), tag(other.tag) { - TRACE_CTOR(annotation_t, "copy"); - } - ~annotation_t() { - TRACE_DTOR(annotation_t); - } - - operator bool() const { - return price || date || tag; - } - - bool operator==(const annotation_t& rhs) const { - return (price == rhs.price && - date == rhs.date && - tag == rhs.tag); - } - - void parse(std::istream& in); - - void print(std::ostream& out, bool keep_base = false) const; - - bool valid() const { - assert(*this); - return true; - } -}; - -struct keep_details_t -{ - bool keep_price; - bool keep_date; - bool keep_tag; - bool only_actuals; - - explicit keep_details_t(bool _keep_price = false, - bool _keep_date = false, - bool _keep_tag = false, - bool _only_actuals = false) - : keep_price(_keep_price), - keep_date(_keep_date), - keep_tag(_keep_tag), - only_actuals(_only_actuals) - { - TRACE_CTOR(keep_details_t, "bool, bool, bool, bool"); - } - keep_details_t(const keep_details_t& other) - : keep_price(other.keep_price), keep_date(other.keep_date), - keep_tag(other.keep_tag), only_actuals(other.only_actuals) { - TRACE_CTOR(keep_details_t, "copy"); - } - ~keep_details_t() throw() { - TRACE_DTOR(keep_details_t); - } - - bool keep_all() const { - return keep_price && keep_date && keep_tag && ! only_actuals; - } - bool keep_all(const commodity_t& comm) const { - return (! comm.annotated || - (keep_price && keep_date && keep_tag && ! only_actuals)); - } - - bool keep_any() const { - return keep_price || keep_date || keep_tag; - } - bool keep_any(const commodity_t& comm) const { - return comm.annotated && (keep_price || keep_date || keep_tag); - } -}; - -inline std::ostream& operator<<(std::ostream& out, - const annotation_t& details) { - details.print(out); - return out; -} - -/** - * @brief Brief - * - * Long. - */ -class annotated_commodity_t - : public commodity_t, - public equality_comparable<annotated_commodity_t, - equality_comparable2<annotated_commodity_t, commodity_t, - noncopyable> > -{ -public: - commodity_t * ptr; - annotation_t details; - - explicit annotated_commodity_t(commodity_t * _ptr, - const annotation_t& _details) - : commodity_t(_ptr->parent_, _ptr->base), ptr(_ptr), details(_details) { - TRACE_CTOR(annotated_commodity_t, ""); - annotated = true; - } - virtual ~annotated_commodity_t() { - TRACE_DTOR(annotated_commodity_t); - } - - virtual bool operator==(const commodity_t& comm) const; - virtual bool operator==(const annotated_commodity_t& comm) const { - return *this == static_cast<const commodity_t&>(comm); - } - - virtual commodity_t& referent() { - return *ptr; - } - virtual const commodity_t& referent() const { - return *ptr; - } - - virtual commodity_t& strip_annotations(const keep_details_t& what_to_keep); - virtual void write_annotations(std::ostream& out) const; -}; - -inline annotated_commodity_t& -as_annotated_commodity(commodity_t& commodity) { - return downcast<annotated_commodity_t>(commodity); -} -inline const annotated_commodity_t& -as_annotated_commodity(const commodity_t& commodity) { - return downcast<const annotated_commodity_t>(commodity); -} - - -/** - * @brief Brief - * - * Long. - */ struct compare_amount_commodities { bool operator()(const amount_t * left, const amount_t * right) const; }; -/** - * @brief Brief - * - * Long. - */ -class commodity_pool_t : public noncopyable -{ - /** - * The commodities collection in commodity_pool_t maintains pointers to all - * the commodities which have ever been created by the user, whether - * explicitly by calling the create methods of commodity_pool_t, or - * implicitly by parsing a commoditized amount. - */ - typedef std::map<string, commodity_t *> commodities_map; - -public: - commodities_map commodities; - - commodity_t * null_commodity; - commodity_t * default_commodity; - - bool keep_base; - -public: - boost::function<optional<amount_t> - (commodity_t& commodity, - const optional<datetime_t>& date, - const optional<datetime_t>& moment, - const optional<datetime_t>& last)> get_quote; - - explicit commodity_pool_t(); - - ~commodity_pool_t() { - TRACE_DTOR(commodity_pool_t); - foreach (commodities_map::value_type pair, commodities) - checked_delete(pair.second); - } - - commodity_t * create(const string& symbol); - commodity_t * find(const string& name); - commodity_t * find_or_create(const string& symbol); - - commodity_t * create(const string& symbol, const annotation_t& details); - commodity_t * find(const string& symbol, const annotation_t& details); - commodity_t * find_or_create(const string& symbol, - const annotation_t& details); - - commodity_t * create(commodity_t& comm, - const annotation_t& details, - const string& mapping_key); - - commodity_t * find_or_create(commodity_t& comm, - const annotation_t& details); - - void parse_commodity_price(char * optarg); -}; - } // namespace ledger #endif // _COMMODITY_H diff --git a/src/derive.cc b/src/derive.cc index 98c50fe1..2fe5754f 100644 --- a/src/derive.cc +++ b/src/derive.cc @@ -98,7 +98,7 @@ namespace { foreach (const post_template_t& post, posts) { straccstream accum; out << std::endl - << ACCUM(accum << _("[Posting \"%1\"]_") + << ACCUM(accum << _("[Posting \"%1\"]") << (post.from ? _("from") : _("to"))) << std::endl; @@ -240,28 +240,30 @@ namespace { report_t& report) { if (tmpl.payee_mask.empty()) - throw std::runtime_error(_("'xact' command requires at least a payee")); - - xact_t * matching = NULL; - journal_t& journal(*report.session.journal.get()); + throw std::runtime_error(_("xact' command requires at least a payee")); + xact_t * matching = NULL; + journal_t& journal(*report.session.journal.get()); std::auto_ptr<xact_t> added(new xact_t); - xacts_list::reverse_iterator j; - - for (j = journal.xacts.rbegin(); + for (xacts_list::reverse_iterator j = journal.xacts.rbegin(); j != journal.xacts.rend(); j++) { if (tmpl.payee_mask.match((*j)->payee)) { matching = *j; + DEBUG("derive.xact", + "Found payee match: transaction on line " << (*j)->beg_line); break; } } - if (! tmpl.date) + if (! tmpl.date) { added->_date = CURRENT_DATE(); - else + DEBUG("derive.xact", "Setting date to current date"); + } else { added->_date = tmpl.date; + DEBUG("derive.xact", "Setting date to template date: " << *tmpl.date); + } added->set_state(item_t::UNCLEARED); @@ -269,17 +271,32 @@ namespace { added->payee = matching->payee; added->code = matching->code; added->note = matching->note; + +#if defined(DEBUG_ON) + DEBUG("derive.xact", "Setting payee from match: " << added->payee); + if (added->code) + DEBUG("derive.xact", "Setting code from match: " << *added->code); + if (added->note) + DEBUG("derive.xact", "Setting note from match: " << *added->note); +#endif } else { added->payee = tmpl.payee_mask.expr.str(); + DEBUG("derive.xact", "Setting payee from template: " << added->payee); } - if (tmpl.code) + if (tmpl.code) { added->code = tmpl.code; - if (tmpl.note) + DEBUG("derive.xact", "Now setting code from template: " << *added->code); + } + if (tmpl.note) { added->note = tmpl.note; + DEBUG("derive.xact", "Now setting note from template: " << *added->note); + } if (tmpl.posts.empty()) { if (matching) { + DEBUG("derive.xact", "Template had no postings, copying from match"); + foreach (post_t * post, matching->posts) { added->add_post(new post_t(*post)); added->posts.back()->set_state(item_t::UNCLEARED); @@ -290,9 +307,12 @@ namespace { << tmpl.payee_mask); } } else { + DEBUG("derive.xact", "Template had postings"); + bool any_post_has_amount = false; foreach (xact_template_t::post_template_t& post, tmpl.posts) { if (post.amount) { + DEBUG("derive.xact", " and at least one has an amount specified"); any_post_has_amount = true; break; } @@ -305,90 +325,149 @@ namespace { if (matching) { if (post.account_mask) { + DEBUG("derive.xact", + "Looking for matching posting based on account mask"); + foreach (post_t * x, matching->posts) { if (post.account_mask->match(x->account->fullname())) { new_post.reset(new post_t(*x)); + DEBUG("derive.xact", + "Founding posting from line " << x->beg_line); break; } } } else { - if (post.from) - new_post.reset(new post_t(*matching->posts.back())); - else - new_post.reset(new post_t(*matching->posts.front())); + if (post.from) { + for (posts_list::reverse_iterator j = matching->posts.rbegin(); + j != matching->posts.rend(); + j++) { + if ((*j)->must_balance()) { + new_post.reset(new post_t(**j)); + DEBUG("derive.xact", + "Copied last real posting from matching"); + break; + } + } + } else { + for (posts_list::iterator j = matching->posts.begin(); + j != matching->posts.end(); + j++) { + if ((*j)->must_balance()) { + new_post.reset(new post_t(**j)); + DEBUG("derive.xact", + "Copied first real posting from matching"); + break; + } + } + } } } - if (! new_post.get()) + if (! new_post.get()) { new_post.reset(new post_t); + DEBUG("derive.xact", "New posting was NULL, creating a blank one"); + } if (! new_post->account) { + DEBUG("derive.xact", "New posting still needs an account"); + if (post.account_mask) { + DEBUG("derive.xact", "The template has an account mask"); + account_t * acct = NULL; - if (! acct) + if (! acct) { acct = journal.find_account_re(post.account_mask->expr.str()); - if (! acct) +#if defined(DEBUG_ON) + if (acct) + DEBUG("derive.xact", "Found account as a regular expression"); +#endif + } + if (! acct) { acct = journal.find_account(post.account_mask->expr.str()); +#if defined(DEBUG_ON) + if (acct) + DEBUG("derive.xact", "Found (or created) account by name"); +#endif + } // Find out the default commodity to use by looking at the last // commodity used in that account - xacts_list::reverse_iterator j; - - for (j = journal.xacts.rbegin(); + for (xacts_list::reverse_iterator j = journal.xacts.rbegin(); j != journal.xacts.rend(); j++) { foreach (post_t * x, (*j)->posts) { if (x->account == acct && ! x->amount.is_null()) { new_post.reset(new post_t(*x)); + DEBUG("derive.xact", + "Found account in journal postings, setting new posting"); break; } } } - if (! new_post.get()) - new_post.reset(new post_t); new_post->account = acct; + DEBUG("derive.xact", + "Set new posting's account to: " << acct->fullname()); } else { - - if (post.from) + if (post.from) { new_post->account = journal.find_account(_("Liabilities:Unknown")); - else + DEBUG("derive.xact", + "Set new posting's account to: Liabilities:Unknown"); + } else { new_post->account = journal.find_account(_("Expenses:Unknown")); + DEBUG("derive.xact", + "Set new posting's account to: Expenses:Unknown"); + } } } if (new_post.get() && ! new_post->amount.is_null()) { found_commodity = &new_post->amount.commodity(); - if (any_post_has_amount) + if (any_post_has_amount) { new_post->amount = amount_t(); - else + DEBUG("derive.xact", "New posting has an amount, but we cleared it"); + } else { any_post_has_amount = true; + DEBUG("derive.xact", "New posting has an amount, and we're using it"); + } } if (post.amount) { new_post->amount = *post.amount; - if (post.from) + DEBUG("derive.xact", "Copied over posting amount"); + + if (post.from) { new_post->amount.in_place_negate(); + DEBUG("derive.xact", "Negated new posting amount"); + } } if (found_commodity && ! new_post->amount.is_null() && ! new_post->amount.has_commodity()) { new_post->amount.set_commodity(*found_commodity); + DEBUG("derive.xact", "Set posting amount commodity to: " + << new_post->amount.commodity()); + new_post->amount = new_post->amount.rounded(); + DEBUG("derive.xact", + "Rounded posting amount to: " << new_post->amount); } added->add_post(new_post.release()); added->posts.back()->set_state(item_t::UNCLEARED); + + DEBUG("derive.xact", "Added new posting to derived entry"); } } if (! journal.xact_finalize_hooks.run_hooks(*added.get(), false) || ! added->finalize() || - ! journal.xact_finalize_hooks.run_hooks(*added.get(), true)) + ! journal.xact_finalize_hooks.run_hooks(*added.get(), true)) { throw_(std::runtime_error, _("Failed to finalize derived transaction (check commodities)")); + } return added.release(); } @@ -423,7 +502,8 @@ value_t xact_command(call_scope_t& args) xact_template_t tmpl = args_to_xact_template(begin, end); std::auto_ptr<xact_t> new_xact(derive_xact_from_template(tmpl, report)); - report.HANDLER(limit_).on("actual"); // jww (2009-02-27): make this more general + // jww (2009-02-27): make this more general + report.HANDLER(limit_).on(string("#xact"), "actual"); report.xact_report(post_handler_ptr (new format_posts(report, diff --git a/src/filters.cc b/src/filters.cc index bb2681c6..967e0843 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -142,6 +142,7 @@ namespace { buf.width(8); buf.fill('0'); buf << std::hex << message_digest[i]; + break; // only output the first dword } return buf.str(); } @@ -175,13 +176,31 @@ void anonymize_posts::operator()(post_t& post) post_t& temp = post_temps.back(); temp.xact = &xact; - sha.Reset(); - sha << post.account->fullname().c_str(); - sha.Result(message_digest); + std::list<string> account_names; + account_t * new_account = NULL; + + for (account_t * acct = post.account; + acct; + acct = acct->parent) { + if (! acct->parent) { + new_account = acct; + break; + } + + sha.Reset(); + sha << acct->name.c_str(); + sha.Result(message_digest); + + account_names.push_front(to_hex(message_digest)); + } + assert(new_account); + + foreach (const string& name, account_names) + new_account = new_account->find_account(name); temp.copy_details(post); - temp.account = post.xact->journal->find_account(to_hex(message_digest)); + temp.account = new_account; temp.note = none; temp.add_flags(ITEM_TEMP); @@ -388,6 +407,15 @@ void related_posts::flush() item_handler<post_t>::flush(); } +void changed_value_posts::flush() +{ + if (last_post && last_post->date() <= report.terminus.date()) { + output_revaluation(last_post, report.terminus.date()); + last_post = NULL; + } + item_handler<post_t>::flush(); +} + void changed_value_posts::output_revaluation(post_t * post, const date_t& date) { if (is_valid(date)) @@ -875,8 +903,7 @@ void forecast_posts::flush() item_handler<post_t>::operator()(temp); - if (temp.has_xdata() && - temp.xdata().has_flags(POST_EXT_MATCHES)) { + if (temp.has_xdata() && temp.xdata().has_flags(POST_EXT_MATCHES)) { bind_scope_t bound_scope(context, temp); if (! pred(bound_scope)) break; diff --git a/src/filters.h b/src/filters.h index 14b97c23..050e3dcf 100644 --- a/src/filters.h +++ b/src/filters.h @@ -468,13 +468,7 @@ public: clear_xacts_posts(xact_temps); } - virtual void flush() { - if (last_post && last_post->date() <= CURRENT_DATE()) { - output_revaluation(last_post, CURRENT_DATE()); - last_post = NULL; - } - item_handler<post_t>::flush(); - } + virtual void flush(); void output_revaluation(post_t * post, const date_t& current); void output_rounding(post_t * post); diff --git a/src/format.cc b/src/format.cc index 4066cabd..ecac1133 100644 --- a/src/format.cc +++ b/src/format.cc @@ -38,6 +38,9 @@ namespace ledger { +format_t::elision_style_t format_t::default_style = TRUNCATE_TRAILING; +bool format_t::default_style_changed = false; + void format_t::element_t::dump(std::ostream& out) const { out << "Element: "; @@ -342,7 +345,8 @@ void format_t::format(std::ostream& out_str, scope_t& scope) } DEBUG("format.expr", "value = (" << value << ")"); - value.print(out, elem->min_width); + value.print(out, elem->min_width, -1, + ! elem->has_flags(ELEMENT_ALIGN_LEFT)); } catch (const calc_error&) { add_error_context(_("While calculating format expression:")); @@ -386,22 +390,22 @@ string format_t::truncate(const unistring& ustr, std::size_t width, std::ostringstream buf; - elision_style_t style = TRUNCATE_TRAILING; - if (account_abbrev_length > 0) + elision_style_t style = default_style; + if (account_abbrev_length > 0 && ! default_style_changed) style = ABBREVIATE; switch (style) { case TRUNCATE_LEADING: // This method truncates at the beginning. - buf << ".." << ustr.extract(len - width, width); + buf << ".." << ustr.extract(len - (width - 2), width - 2); break; case TRUNCATE_MIDDLE: // This method truncates in the middle. - buf << ustr.extract(0, width / 2) + buf << ustr.extract(0, (width - 2) / 2) << ".." - << ustr.extract(len - (width / 2 + width % 2), - width / 2 + width % 2); + << ustr.extract(len - ((width - 2) / 2 + (width - 2) % 2), + (width - 2) / 2 + (width - 2) % 2); break; case ABBREVIATE: diff --git a/src/format.h b/src/format.h index 13dd0876..03ed28c7 100644 --- a/src/format.h +++ b/src/format.h @@ -107,12 +107,14 @@ class format_t : public noncopyable scoped_ptr<element_t> elements; public: - enum elision_style_t { + static enum elision_style_t { TRUNCATE_TRAILING, TRUNCATE_MIDDLE, TRUNCATE_LEADING, ABBREVIATE - }; + } default_style; + + static bool default_style_changed; private: static element_t * parse_elements(const string& fmt); diff --git a/src/global.cc b/src/global.cc index a258d0bb..24716fae 100644 --- a/src/global.cc +++ b/src/global.cc @@ -39,9 +39,12 @@ #endif #include "item.h" #include "journal.h" +#include "pool.h" namespace ledger { +static bool args_only = false; + global_scope_t::global_scope_t(char ** envp) { TRACE_CTOR(global_scope_t, ""); @@ -74,11 +77,13 @@ global_scope_t::global_scope_t(char ** envp) // Before processing command-line options, we must notify the session object // that such options are beginning, since options like -f cause a complete // override of files found anywhere else. - if (! HANDLED(args_only)) { + if (! args_only) { session().set_flush_on_next_data_file(true); read_environment_settings(envp); session().set_flush_on_next_data_file(true); read_init(); + } else { + session().HANDLER(price_db_).off(); } } @@ -138,7 +143,7 @@ void global_scope_t::report_error(const std::exception& err) string context = error_context(); if (! context.empty()) std::cerr << context << std::endl; - + std::cerr << _("Error: ") << err.what() << std::endl; } else { caught_signal = NONE_CAUGHT; @@ -179,8 +184,6 @@ void global_scope_t::execute_command(strings_list args, bool at_repl) if (bool(command = look_for_precommand(bound_scope, verb))) is_precommand = true; - else if (! bool(command = look_for_command(bound_scope, verb))) - throw_(std::logic_error, _("Unrecognized command '%1'") << verb); // If it is not a pre-command, then parse the user's ledger data at this // time if not done alreday (i.e., if not at a REPL). Then patch up the @@ -189,7 +192,11 @@ void global_scope_t::execute_command(strings_list args, bool at_repl) if (! is_precommand) { if (! at_repl) session().read_journal_files(); + normalize_report_options(verb); + + if (! bool(command = look_for_command(bound_scope, verb))) + throw_(std::logic_error, _("Unrecognized command '%1'") << verb); } // Create the output stream (it might be a file, the console or a PAGER @@ -204,6 +211,12 @@ void global_scope_t::execute_command(strings_list args, bool at_repl) optional<path>(path(report().HANDLER(pager_).str())) : optional<path>()); + // Now that the output stream is initialized, report the options that will + // participate in this report, if the user specified --options + + if (HANDLED(options)) + report_options(report(), report().output_stream); + // Create an argument scope containing the report command's arguments, and // then invoke the command. The bound scope causes lookups to happen // first in the global scope, and then in the report scope. @@ -239,6 +252,29 @@ int global_scope_t::execute_command_wrapper(strings_list args, bool at_repl) return status; } +void global_scope_t::report_options(report_t& report, std::ostream& out) +{ + out << "<=============================================================================>" + << std::endl; + out << "[Global scope options]" << std::endl; + + HANDLER(args_only).report(out); + HANDLER(debug_).report(out); + HANDLER(init_file_).report(out); + HANDLER(script_).report(out); + HANDLER(trace_).report(out); + HANDLER(verbose).report(out); + HANDLER(verify).report(out); + + out << std::endl << "[Session scope options]" << std::endl; + report.session.report_options(out); + + out << std::endl << "[Report scope options]" << std::endl; + report.report_options(out); + out << "<=============================================================================>" + << std::endl; +} + option_t<global_scope_t> * global_scope_t::lookup_option(const char * p) { switch (*p) { @@ -260,6 +296,9 @@ option_t<global_scope_t> * global_scope_t::lookup_option(const char * p) case 'i': OPT(init_file_); break; + case 'o': + OPT(options); + break; case 's': OPT(script_); break; @@ -287,13 +326,13 @@ expr_t::ptr_op_t global_scope_t::lookup(const string& name) break; case 'p': - if (WANT_PRECMD()) { p += PRECMD_PREFIX_LEN; - switch (*p) { + if (WANT_PRECMD()) { const char * q = p + PRECMD_PREFIX_LEN; + switch (*q) { case 'p': - if (is_eq(p, "push")) - MAKE_FUNCTOR(global_scope_t::push_command); - else if (is_eq(p, "pop")) - MAKE_FUNCTOR(global_scope_t::pop_command); + if (is_eq(q, "push")) + return MAKE_FUNCTOR(global_scope_t::push_command); + else if (is_eq(q, "pop")) + return MAKE_FUNCTOR(global_scope_t::pop_command); break; } } @@ -316,18 +355,18 @@ void global_scope_t::read_environment_settings(char * envp[]) if (const char * p = std::getenv("LEDGER")) { if (! std::getenv("LEDGER_FILE")) - process_option("file", report(), p, "LEDGER"); + process_option("environ", "file", report(), p, "LEDGER"); } if (const char * p = std::getenv("LEDGER_INIT")) { if (! std::getenv("LEDGER_INIT_FILE")) - process_option("init-file", report(), p, "LEDGER_INIT"); + process_option("environ", "init-file", report(), p, "LEDGER_INIT"); } if (const char * p = std::getenv("PRICE_HIST")) { if (! std::getenv("LEDGER_PRICEDB")) - process_option("price-db", report(), p, "PRICE_HIST"); + process_option("environ", "price-db", report(), p, "PRICE_HIST"); } if (const char * p = std::getenv("PRICE_EXP")) - process_option("price-exp", report(), p, "PRICE_EXP"); + process_option("environ", "price-exp", report(), p, "PRICE_EXP"); #endif TRACE_FINISH(environment, 1); @@ -379,9 +418,19 @@ void global_scope_t::normalize_report_options(const string& verb) report_t& rep(report()); - // jww (2009-02-09): These global are a hack, but hard to avoid. - item_t::use_effective_date = rep.HANDLED(effective); - rep.session.commodity_pool->keep_base = rep.HANDLED(base); + // jww (2009-02-09): These globals are a hack, but hard to avoid. + item_t::use_effective_date = rep.HANDLED(effective); + rep.session.commodity_pool->keep_base = rep.HANDLED(base); + rep.session.commodity_pool->get_quotes = rep.session.HANDLED(download); + + if (rep.session.HANDLED(price_exp_)) + rep.session.commodity_pool->quote_leeway = + rep.session.HANDLER(price_exp_).value.as_long(); + + if (rep.session.HANDLED(price_db_)) + rep.session.commodity_pool->price_db = rep.session.HANDLER(price_db_).str(); + else + rep.session.commodity_pool->price_db = none; if (rep.HANDLED(date_format_)) { output_datetime_format = rep.HANDLER(date_format_).str() + " %H:%M:%S"; @@ -397,29 +446,75 @@ void global_scope_t::normalize_report_options(const string& verb) // I might be able to do it with command objects, like register_t, which // each know how to adjust the report based on its current option settings. if (verb == "print" || verb == "xact" || verb == "dump") { - rep.HANDLER(related).on_only(); - rep.HANDLER(related_all).on_only(); + rep.HANDLER(related).on_only(string("?normalize")); + rep.HANDLER(related_all).on_only(string("?normalize")); } else if (verb == "equity") { - rep.HANDLER(equity).on_only(); + rep.HANDLER(equity).on_only(string("?normalize")); } else if (rep.HANDLED(related)) { if (verb[0] == 'r') { - rep.HANDLER(invert).on_only(); + rep.HANDLER(invert).on_only(string("?normalize")); } else { - rep.HANDLER(subtotal).on_only(); - rep.HANDLER(related_all).on_only(); + rep.HANDLER(subtotal).on_only(string("?normalize")); + rep.HANDLER(related_all).on_only(string("?normalize")); } } + if (verb == "print") + rep.HANDLER(limit_).on(string("?normalize"), "actual"); + if (! rep.HANDLED(empty)) - rep.HANDLER(display_).on("amount|(!post&total)"); + rep.HANDLER(display_).on(string("?normalize"), "amount|(!post&total)"); if (verb[0] != 'b' && verb[0] != 'r') - rep.HANDLER(base).on_only(); + rep.HANDLER(base).on_only(string("?normalize")); + + // If a time period was specified with -p, check whether it also gave a + // begin and/or end to the report period (though these can be overridden + // using -b or -e). Then, if no _duration_ was specified (such as monthly), + // then ignore the period since the begin/end are the only interesting + // details. + if (rep.HANDLED(period_)) { + if (! rep.HANDLED(sort_all_)) + rep.HANDLER(sort_xacts_).on_only(string("?normalize")); + + date_interval_t interval(rep.HANDLER(period_).str()); + + if (! rep.HANDLED(begin_) && interval.start) { + string predicate = + "date>=[" + to_iso_extended_string(*interval.start) + "]"; + rep.HANDLER(limit_).on(string("?normalize"), predicate); + } + if (! rep.HANDLED(end_) && interval.end) { + string predicate = + "date<[" + to_iso_extended_string(*interval.end) + "]"; + rep.HANDLER(limit_).on(string("?normalize"), predicate); + } + + if (! interval.duration) + rep.HANDLER(period_).off(); + } + + // If -j or -J were specified, set the appropriate format string now so as + // to avoid option ordering issues were we to have done it during the + // initial parsing of the options. + if (rep.HANDLED(amount_data)) { + rep.HANDLER(format_) + .on_with(string("?normalize"), rep.HANDLER(plot_amount_format_).value); + } + else if (rep.HANDLED(total_data)) { + rep.HANDLER(format_) + .on_with(string("?normalize"), rep.HANDLER(plot_total_format_).value); + } - if (rep.HANDLED(period_) && ! rep.HANDLED(sort_all_)) - rep.HANDLER(sort_xacts_).on_only(); + // If the --exchange (-X) option was used, parse out any final price + // settings that may be there. + if (rep.HANDLED(exchange_) && + rep.HANDLER(exchange_).str().find('=') != string::npos) { + value_t(0L).exchange_commodities(rep.HANDLER(exchange_).str(), true, + rep.terminus); + } long cols = 0; if (rep.HANDLED(columns_)) @@ -465,15 +560,15 @@ void global_scope_t::normalize_report_options(const string& verb) } if (! rep.HANDLER(date_width_).specified) - rep.HANDLER(date_width_).on_with(date_width); + rep.HANDLER(date_width_).on_with(string("?normalize"), date_width); if (! rep.HANDLER(payee_width_).specified) - rep.HANDLER(payee_width_).on_with(payee_width); + rep.HANDLER(payee_width_).on_with(string("?normalize"), payee_width); if (! rep.HANDLER(account_width_).specified) - rep.HANDLER(account_width_).on_with(account_width); + rep.HANDLER(account_width_).on_with(string("?normalize"), account_width); if (! rep.HANDLER(amount_width_).specified) - rep.HANDLER(amount_width_).on_with(amount_width); + rep.HANDLER(amount_width_).on_with(string("?normalize"), amount_width); if (! rep.HANDLER(total_width_).specified) - rep.HANDLER(total_width_).on_with(total_width); + rep.HANDLER(total_width_).on_with(string("?normalize"), total_width); } } @@ -500,7 +595,10 @@ void handle_debug_options(int argc, char * argv[]) { for (int i = 1; i < argc; i++) { if (argv[i][0] == '-') { - if (std::strcmp(argv[i], "--verify") == 0) { + if (std::strcmp(argv[i], "--args-only") == 0) { + args_only = true; + } + else if (std::strcmp(argv[i], "--verify") == 0) { #if defined(VERIFY_ON) verify_enabled = true; // global in utils.h #endif diff --git a/src/global.h b/src/global.h index 2ef5a545..df9845d5 100644 --- a/src/global.h +++ b/src/global.h @@ -113,6 +113,8 @@ See LICENSE file included with the distribution for details and disclaimer."); out << std::endl; } + void report_options(report_t& report, std::ostream& out); + option_t<global_scope_t> * lookup_option(const char * p); virtual expr_t::ptr_op_t lookup(const string& name); @@ -133,11 +135,12 @@ See LICENSE file included with the distribution for details and disclaimer."); CTOR(global_scope_t, init_file_) { if (const char * home_var = std::getenv("HOME")) - on((path(home_var) / ".ledgerrc").string()); + on(none, (path(home_var) / ".ledgerrc").string()); else - on(path("./.ledgerrc").string()); + on(none, path("./.ledgerrc").string()); }); + OPTION(global_scope_t, options); OPTION(global_scope_t, script_); OPTION(global_scope_t, trace_); OPTION(global_scope_t, verbose); diff --git a/src/interactive.cc b/src/interactive.cc index 528ef9f2..1ae976e7 100644 --- a/src/interactive.cc +++ b/src/interactive.cc @@ -75,7 +75,13 @@ void interactive_t::verify_arguments() const break; case 'd': label = _("a date"); - wrong_arg = ! next_arg->is_date(); + wrong_arg = (! next_arg->is_date() && + ! next_arg->is_datetime()); + break; + case 't': + label = _("a date/time"); + wrong_arg = (! next_arg->is_date() && + ! next_arg->is_datetime()); break; case 'i': case 'l': @@ -106,11 +112,6 @@ void interactive_t::verify_arguments() const label = _("a string"); wrong_arg = ! next_arg->is_string(); break; - case 't': - label = _("a date or time"); - wrong_arg = (! next_arg->is_date() && - ! next_arg->is_datetime()); - break; case 'v': label = _("any value"); wrong_arg = false; diff --git a/src/item.cc b/src/item.cc index 758ce254..9e297052 100644 --- a/src/item.cc +++ b/src/item.cc @@ -247,6 +247,10 @@ namespace { return 0L; } + value_t ignore(item_t&) { + return false; + } + template <value_t (*Func)(item_t&)> value_t get_wrapper(call_scope_t& scope) { return (*Func)(find_scope<item_t>(scope)); @@ -258,10 +262,12 @@ value_t get_comment(item_t& item) if (! item.note) { return string_value(""); } else { - // jww (2009-03-01): If the comment is a short one-liner, put it at the - // end of the post/xact std::ostringstream buf; - buf << "\n ;"; + if (item.note->length() > 15) + buf << "\n ;"; + else + buf << " ;"; + bool need_separator = false; for (const char * p = item.note->c_str(); *p; p++) { if (*p == '\n') { @@ -328,6 +334,11 @@ expr_t::ptr_op_t item_t::lookup(const string& name) return WRAP_FUNCTOR(ledger::has_tag); break; + case 'i': + if (name == "is_account") + return WRAP_FUNCTOR(get_wrapper<&ignore>); + break; + case 'm': if (name == "meta") return WRAP_FUNCTOR(ledger::get_tag); @@ -341,6 +352,8 @@ expr_t::ptr_op_t item_t::lookup(const string& name) case 'p': if (name == "pending") return WRAP_FUNCTOR(get_wrapper<&get_pending>); + else if (name == "parent") + return WRAP_FUNCTOR(get_wrapper<&ignore>); break; case 's': diff --git a/src/iterators.cc b/src/iterators.cc index 9b07ace3..63cbb9b2 100644 --- a/src/iterators.cc +++ b/src/iterators.cc @@ -101,7 +101,7 @@ void posts_commodities_iterator::reset(journal_t& journal) foreach (commodity_t::base_t::history_map::value_type hpair, pair.second.prices) { xact_t * xact; - string symbol = hpair.second.commodity().symbol(); + string symbol = hpair.second.commodity().symbol(); std::map<string, xact_t *>::iterator i = xacts_by_commodity.find(symbol); @@ -123,6 +123,8 @@ void posts_commodities_iterator::reset(journal_t& journal) temp.amount = hpair.second; temp.set_flags(ITEM_GENERATED | ITEM_TEMP); + temp.xdata().datetime = hpair.first; + xact->add_post(&temp); } } diff --git a/src/iterators.h b/src/iterators.h index 67293c5c..ae2ddaf9 100644 --- a/src/iterators.h +++ b/src/iterators.h @@ -175,7 +175,7 @@ protected: std::list<post_t> post_temps; std::list<account_t> acct_temps; - xacts_list xact_temps; + xacts_list xact_temps; public: posts_commodities_iterator() { diff --git a/src/ledger.h b/src/ledger.h deleted file mode 100644 index d37fddaa..00000000 --- a/src/ledger.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * - Neither the name of New Artisans LLC nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @file ledger.h - * @author John Wiegley - * - * @mainpage Ledger Accounting Tool - * - * There are essentially nine steps involved in realizing a ledger reporting - * session, with steps 5 and 8 -- which relate to the user's journal file -- - * being optional in the case of "pre-commands", since they do not require the - * user's data to be read. - * - * \section global_init Initialize the global environment - * - * \section create_objs Create session and report objects - * - * jww (2009-02-02): Set the "session context". - * - * \section process_opts Process user options - * - * This configures session and report objects - * - * - environment - * - initialization file - * - command-line options - * - * \section lookup_cmd Locate object relating to command verb - * - * \section parse_data Parse the user's journal files - * - * \section create_out Create the output stream - * - * \section invoke_cmd Invoke the command object - * - * \section shutdown Wrap up, closing everything and releasing memory - */ -#ifndef _LEDGER_H -#define _LEDGER_H - - - - - - -#if defined(HAVE_BOOST_PYTHON) -#define LEDGER_SESSION_T python_interpreter_t -#else -#define LEDGER_SESSION_T session_t -#endif - -#endif // _LEDGER_H @@ -34,6 +34,7 @@ #include "op.h" #include "scope.h" #include "commodity.h" +#include "pool.h" namespace ledger { @@ -166,10 +167,15 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus) call_scope_t call_args(scope); if (value_t obj = left()->left()->as_function()(call_args)) { if (obj.is_pointer()) { - scope_t& objscope(obj.as_ref_lval<scope_t>()); - if (ptr_op_t member = objscope.lookup(right()->as_ident())) { - result = member->calc(objscope); - break; + if (obj.as_pointer_lval<scope_t>() == NULL) { + throw_(calc_error, + _("Left operand of . operator is NULL")); + } else { + scope_t& objscope(obj.as_ref_lval<scope_t>()); + if (ptr_op_t member = objscope.lookup(right()->as_ident())) { + result = member->calc(objscope); + break; + } } } } diff --git a/src/option.cc b/src/option.cc index 7d5b272d..1275e270 100644 --- a/src/option.cc +++ b/src/option.cc @@ -76,11 +76,13 @@ namespace { return op_bool_tuple(scope.lookup(buf), false); } - void process_option(const function_t& opt, scope_t& scope, - const char * arg, const string& name) + void process_option(const string& whence, const function_t& opt, + scope_t& scope, const char * arg, const string& name) { try { call_scope_t args(scope); + + args.push_back(string_value(whence)); if (arg) args.push_back(string_value(arg)); @@ -97,12 +99,12 @@ namespace { } } -void process_option(const string& name, scope_t& scope, +void process_option(const string& whence, const string& name, scope_t& scope, const char * arg, const string& varname) { op_bool_tuple opt(find_option(scope, name)); if (opt.first) - process_option(opt.first->as_function(), scope, arg, varname); + process_option(whence, opt.first->as_function(), scope, arg, varname); } void process_environment(const char ** envp, const string& tag, @@ -129,7 +131,7 @@ void process_environment(const char ** envp, const string& tag, try { string value = string(*p, q - *p); if (! value.empty()) - process_option(string(buf), scope, q + 1, value); + process_option(string("$") + buf, string(buf), scope, q + 1, value); } catch (const std::exception& err) { add_error_context(_("While parsing environment variable option '%1':") @@ -201,7 +203,8 @@ strings_list process_arguments(strings_list args, scope_t& scope) if (value == NULL) throw_(option_error, _("Missing option argument for --%1") << name); } - process_option(opt.first->as_function(), scope, value, + process_option(string("--") + name, + opt.first->as_function(), scope, value, string("--") + name); } else if ((*i)[1] == '\0') { @@ -230,7 +233,8 @@ strings_list process_arguments(strings_list args, scope_t& scope) throw_(option_error, _("Missing option argument for -%1") << o.ch); } - process_option(o.op->as_function(), scope, value, string("-") + o.ch); + process_option(string("-") + o.ch, o.op->as_function(), scope, value, + string("-") + o.ch); } } } diff --git a/src/option.h b/src/option.h index 86899931..6809a27c 100644 --- a/src/option.h +++ b/src/option.h @@ -56,10 +56,11 @@ template <typename T> class option_t { protected: - const char * name; - std::size_t name_len; - const char ch; - bool handled; + const char * name; + std::size_t name_len; + const char ch; + bool handled; + optional<string> source; option_t& operator=(const option_t&); @@ -73,6 +74,7 @@ public: handled(false), parent(NULL), value(), wants_arg(name[name_len - 1] == '_') { TRACE_CTOR(option_t, "const char *, const char"); + DEBUG("option.names", "Option: " << name); } option_t(const option_t& other) : name(other.name), @@ -90,6 +92,18 @@ public: TRACE_DTOR(option_t); } + void report(std::ostream& out) const { + if (handled && source) { + if (wants_arg) { + out << desc() << " => "; + value.dump(out); + } else { + out << desc(); + } + out << " <" << *source << ">" << std::endl; + } + } + string desc() const { std::ostringstream out; out << "--"; @@ -117,31 +131,42 @@ public: return value.as_string_lval(); } - void on_only() { + string str() const { + assert(handled); + if (! value) + throw_(std::runtime_error, _("No argument provided for %1") << desc()); + return value.as_string(); + } + + void on_only(const optional<string>& whence) { handled = true; + source = whence; } - void on(const string& str) { - on_with(string_value(str)); + void on(const optional<string>& whence, const string& str) { + on_with(whence, string_value(str)); } - virtual void on_with(const value_t& val) { + virtual void on_with(const optional<string>& whence, + const value_t& val) { handled = true; value = val; + source = whence; } void off() { handled = false; value = value_t(); + source = none; } virtual void handler_thunk(call_scope_t&) {} virtual void handler(call_scope_t& args) { if (wants_arg) { - if (args.empty()) + if (args.empty() || args.size() == 1) throw_(std::runtime_error, _("No argument provided for %1") << desc()); - on_with(args[0]); + on_with(args[0].as_string(), args[1]); } else { - on_only(); + on_only(args[0].as_string()); } handler_thunk(args); @@ -270,7 +295,7 @@ inline bool is_eq(const char * p, const char * n) { #define WANT_DIR() \ (std::strncmp(p, DIR_PREFIX, DIR_PREFIX_LEN) == 0) -void process_option(const string& name, scope_t& scope, +void process_option(const string& whence, const string& name, scope_t& scope, const char * arg, const string& varname); void process_environment(const char ** envp, const string& tag, diff --git a/src/output.cc b/src/output.cc index 63d8c919..92824714 100644 --- a/src/output.cc +++ b/src/output.cc @@ -46,7 +46,7 @@ format_posts::format_posts(report_t& _report, : report(_report), last_xact(NULL), last_post(NULL), print_raw(_print_raw) { - TRACE_CTOR(format_posts, "report&, const string&"); + TRACE_CTOR(format_posts, "report&, const string&, bool"); const char * f = format.c_str(); @@ -179,7 +179,9 @@ format_accounts::mark_accounts(account_t& account, const bool flat) (account.has_flags(ACCOUNT_EXT_VISITED) || (! flat && visited > 0))) { bind_scope_t bound_scope(report, account); if ((! flat && to_display > 1) || - (disp_pred(bound_scope) && (flat || to_display != 1))) { + ((flat || to_display != 1 || + account.has_flags(ACCOUNT_EXT_VISITED)) && + disp_pred(bound_scope))) { account.xdata().add_flags(ACCOUNT_EXT_TO_DISPLAY); DEBUG("account.display", "Marking account as TO_DISPLAY"); to_display = 1; @@ -207,7 +209,8 @@ void format_accounts::flush() foreach (account_t * account, posted_accounts) displayed += post_account(*account); - if (! report.HANDLED(no_total) && displayed > 1) { + if (displayed > 1 && + ! report.HANDLED(no_total) && ! report.HANDLED(percent)) { bind_scope_t bound_scope(report, *report.session.master); separator_format.format(out, bound_scope); total_line_format.format(out, bound_scope); diff --git a/src/parser.cc b/src/parser.cc index 1c78e30d..39004758 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -44,23 +44,10 @@ expr_t::parser_t::parse_value_term(std::istream& in, token_t& tok = next_token(in, tflags); switch (tok.kind) { - case token_t::VALUE: { + case token_t::VALUE: node = new op_t(op_t::VALUE); node->set_value(tok.value); - - token_t& postfix_tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT)); - if (postfix_tok.kind == token_t::PERCENT) { - ptr_op_t prev(node); - node = new op_t(op_t::O_DIV); - node->set_left(prev); - ptr_op_t hundred = new op_t(op_t::VALUE); - hundred->set_value(100L); - node->set_right(hundred); - } else { - push_token(postfix_tok); - } break; - } case token_t::IDENT: { string ident = tok.value.as_string(); diff --git a/src/pool.cc b/src/pool.cc new file mode 100644 index 00000000..68ba3ed7 --- /dev/null +++ b/src/pool.cc @@ -0,0 +1,361 @@ +/* + * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of New Artisans LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <system.hh> + +#include "amount.h" +#include "commodity.h" +#include "annotate.h" +#include "pool.h" +#include "quotes.h" + +namespace ledger { + +commodity_pool_t::commodity_pool_t() + : default_commodity(NULL), keep_base(false), + quote_leeway(86400), get_quotes(false), + get_commodity_quote(commodity_quote_from_script) +{ + TRACE_CTOR(commodity_pool_t, ""); + null_commodity = create(""); + null_commodity->add_flags(COMMODITY_BUILTIN | COMMODITY_NOMARKET); +} + +commodity_t * commodity_pool_t::create(const string& symbol) +{ + shared_ptr<commodity_t::base_t> + base_commodity(new commodity_t::base_t(symbol)); + std::auto_ptr<commodity_t> commodity(new commodity_t(this, base_commodity)); + + DEBUG("amounts.commodities", "Creating base commodity " << symbol); + + // Create the "qualified symbol" version of this commodity's symbol + if (commodity_t::symbol_needs_quotes(symbol)) { + commodity->qualified_symbol = "\""; + *commodity->qualified_symbol += symbol; + *commodity->qualified_symbol += "\""; + } + + DEBUG("amounts.commodities", + "Creating commodity '" << commodity->symbol() << "'"); + + std::pair<commodities_map::iterator, bool> result + = commodities.insert(commodities_map::value_type(commodity->mapping_key(), + commodity.get())); + assert(result.second); + + return commodity.release(); +} + +commodity_t * commodity_pool_t::find_or_create(const string& symbol) +{ + DEBUG("amounts.commodities", "Find-or-create commodity " << symbol); + + commodity_t * commodity = find(symbol); + if (commodity) + return commodity; + return create(symbol); +} + +commodity_t * commodity_pool_t::find(const string& symbol) +{ + DEBUG("amounts.commodities", "Find commodity " << symbol); + + commodities_map::const_iterator i = commodities.find(symbol); + if (i != commodities.end()) + return (*i).second; + return NULL; +} + +commodity_t * +commodity_pool_t::create(const string& symbol, const annotation_t& details) +{ + commodity_t * new_comm = create(symbol); + if (! new_comm) + return NULL; + + if (details) + return find_or_create(*new_comm, details); + else + return new_comm; +} + +namespace { + string make_qualified_name(const commodity_t& comm, + const annotation_t& details) + { + assert(details); + + if (details.price && details.price->sign() < 0) + throw_(amount_error, _("A commodity's price may not be negative")); + + std::ostringstream name; + comm.print(name); + details.print(name, comm.parent().keep_base); + + DEBUG("amounts.commodities", "make_qualified_name for " + << *comm.qualified_symbol << std::endl << details); + DEBUG("amounts.commodities", "qualified_name is " << name.str()); + + return name.str(); + } +} + +commodity_t * +commodity_pool_t::find(const string& symbol, const annotation_t& details) +{ + commodity_t * comm = find(symbol); + if (! comm) + return NULL; + + if (details) { + string name = make_qualified_name(*comm, details); + + if (commodity_t * ann_comm = find(name)) { + assert(ann_comm->annotated && as_annotated_commodity(*ann_comm).details); + return ann_comm; + } + return NULL; + } else { + return comm; + } +} + +commodity_t * +commodity_pool_t::find_or_create(const string& symbol, + const annotation_t& details) +{ + commodity_t * comm = find(symbol); + if (! comm) + return NULL; + + if (details) + return find_or_create(*comm, details); + else + return comm; +} + +commodity_t * +commodity_pool_t::create(commodity_t& comm, + const annotation_t& details, + const string& mapping_key) +{ + assert(comm); + assert(details); + assert(! mapping_key.empty()); + + std::auto_ptr<commodity_t> commodity + (new annotated_commodity_t(&comm, details)); + + commodity->qualified_symbol = comm.symbol(); + assert(! commodity->qualified_symbol->empty()); + + DEBUG("amounts.commodities", "Creating annotated commodity " + << "symbol " << commodity->symbol() + << " key " << mapping_key << std::endl << details); + + // Add the fully annotated name to the map, so that this symbol may + // quickly be found again. + commodity->mapping_key_ = mapping_key; + + std::pair<commodities_map::iterator, bool> result + = commodities.insert(commodities_map::value_type(mapping_key, + commodity.get())); + assert(result.second); + + return commodity.release(); +} + +commodity_t * commodity_pool_t::find_or_create(commodity_t& comm, + const annotation_t& details) +{ + assert(comm); + assert(details); + + string name = make_qualified_name(comm, details); + assert(! name.empty()); + + if (commodity_t * ann_comm = find(name)) { + assert(ann_comm->annotated && as_annotated_commodity(*ann_comm).details); + return ann_comm; + } + return create(comm, details, name); +} + +void commodity_pool_t::exchange(commodity_t& commodity, + const amount_t& per_unit_cost, + const datetime_t& moment) +{ + DEBUG("commodity.prices.add", "exchanging commodity " << commodity + << " at per unit cost " << per_unit_cost << " on " << moment); + + commodity_t& base_commodity + (commodity.annotated ? + as_annotated_commodity(commodity).referent() : commodity); + + base_commodity.add_price(moment, per_unit_cost); +} + +cost_breakdown_t +commodity_pool_t::exchange(const amount_t& amount, + const amount_t& cost, + const bool is_per_unit, + const optional<datetime_t>& moment, + const optional<string>& tag) +{ + DEBUG("commodity.prices.add", "exchange: " << amount << " for " << cost); + DEBUG("commodity.prices.add", "exchange: is-per-unit = " << is_per_unit); +#if defined(DEBUG_ON) + if (moment) + DEBUG("commodity.prices.add", "exchange: moment = " << *moment); + if (tag) + DEBUG("commodity.prices.add", "exchange: tag = " << *tag); +#endif + + commodity_t& commodity(amount.commodity()); + + annotation_t * current_annotation = NULL; + if (commodity.annotated) + current_annotation = &as_annotated_commodity(commodity).details; + + amount_t per_unit_cost = + (is_per_unit || amount.is_realzero() ? cost : cost / amount).abs(); + + DEBUG("commodity.prices.add", "exchange: per-unit-cost = " << per_unit_cost); + + if (! per_unit_cost.is_realzero()) + exchange(commodity, per_unit_cost, moment ? *moment : CURRENT_TIME()); + + cost_breakdown_t breakdown; + breakdown.final_cost = ! is_per_unit ? cost : cost * amount; + + DEBUG("commodity.prices.add", + "exchange: final-cost = " << breakdown.final_cost); + + if (current_annotation && current_annotation->price) + breakdown.basis_cost + = (*current_annotation->price * amount).unrounded(); + else + breakdown.basis_cost = breakdown.final_cost; + + DEBUG("commodity.prices.add", + "exchange: basis-cost = " << breakdown.basis_cost); + + annotation_t annotation(per_unit_cost, moment ? + moment->date() : optional<date_t>(), tag); + + annotation.add_flags(ANNOTATION_PRICE_CALCULATED); + if (moment) + annotation.add_flags(ANNOTATION_DATE_CALCULATED); + if (tag) + annotation.add_flags(ANNOTATION_TAG_CALCULATED); + + breakdown.amount = amount_t(amount, annotation); + + DEBUG("commodity.prices.add", + "exchange: amount = " << breakdown.amount); + + return breakdown; +} + +optional<price_point_t> commodity_pool_t::parse_price_directive(char * line) +{ + char * date_field_ptr = line; + char * time_field_ptr = next_element(date_field_ptr); + if (! time_field_ptr) return none; + string date_field = date_field_ptr; + + char * symbol_and_price; + datetime_t datetime; + + if (std::isdigit(time_field_ptr[0])) { + symbol_and_price = next_element(time_field_ptr); + if (! symbol_and_price) return none; + + datetime = parse_datetime(date_field + " " + time_field_ptr); + } + else if (std::isdigit(date_field_ptr[0])) { + symbol_and_price = time_field_ptr; + datetime = datetime_t(parse_date(date_field)); + } + else { + symbol_and_price = date_field_ptr; + datetime = CURRENT_TIME(); + } + + string symbol; + commodity_t::parse_symbol(symbol_and_price, symbol); + + price_point_t point; + point.when = datetime; + point.price.parse(symbol_and_price); + VERIFY(point.price.valid()); + + DEBUG("commodity.download", "Looking up symbol: " << symbol); + if (commodity_t * commodity = + amount_t::current_pool->find_or_create(symbol)) { + DEBUG("commodity.download", "Adding price for " << symbol << ": " + << point.when << " " << point.price); + commodity->add_price(point.when, point.price, true); + commodity->add_flags(COMMODITY_KNOWN); + return point; + } + + return none; +} + +commodity_t * +commodity_pool_t::parse_price_expression(const std::string& str, + const bool add_prices, + const optional<datetime_t>& moment) +{ + scoped_array<char> buf(new char[str.length() + 1]); + + std::strcpy(buf.get(), str.c_str()); + + char * price = std::strchr(buf.get(), '='); + if (price) + *price++ = '\0'; + + if (commodity_t * commodity = find_or_create(trim_ws(buf.get()))) { + if (price && add_prices) { + for (char * p = std::strtok(price, ";"); + p; + p = std::strtok(NULL, ";")) { + commodity->add_price(moment ? *moment : CURRENT_TIME(), amount_t(p)); + } + } + return commodity; + } + return NULL; +} + +} // namespace ledger diff --git a/src/pool.h b/src/pool.h new file mode 100644 index 00000000..378163e9 --- /dev/null +++ b/src/pool.h @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of New Artisans LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @addtogroup math + */ + +/** + * @file pool.h + * @author John Wiegley + * + * @ingroup math + * + * @brief Types for managing commodity pools + * + * Long. + */ +#ifndef _POOL_H +#define _POOL_H + +namespace ledger { + +/** + * @brief Brief + * + * Long. + */ +struct cost_breakdown_t +{ + amount_t amount; + amount_t final_cost; + amount_t basis_cost; +}; + +/** + * @brief Brief + * + * Long. + */ +class commodity_pool_t : public noncopyable +{ + /** + * The commodities collection in commodity_pool_t maintains pointers to all + * the commodities which have ever been created by the user, whether + * explicitly by calling the create methods of commodity_pool_t, or + * implicitly by parsing a commoditized amount. + */ + typedef std::map<string, commodity_t *> commodities_map; + +public: + commodities_map commodities; + commodity_t * null_commodity; + commodity_t * default_commodity; + + bool keep_base; // --base + + optional<path> price_db; // --price-db= + long quote_leeway; // --leeway= + bool get_quotes; // --download + + function<optional<price_point_t> + (commodity_t& commodity, const optional<commodity_t&>& in_terms_of)> + get_commodity_quote; + + explicit commodity_pool_t(); + + ~commodity_pool_t() { + TRACE_DTOR(commodity_pool_t); + foreach (commodities_map::value_type pair, commodities) + checked_delete(pair.second); + } + + commodity_t * create(const string& symbol); + commodity_t * find(const string& name); + commodity_t * find_or_create(const string& symbol); + + commodity_t * create(const string& symbol, const annotation_t& details); + commodity_t * find(const string& symbol, const annotation_t& details); + commodity_t * find_or_create(const string& symbol, + const annotation_t& details); + + commodity_t * create(commodity_t& comm, + const annotation_t& details, + const string& mapping_key); + + commodity_t * find_or_create(commodity_t& comm, + const annotation_t& details); + + // Exchange one commodity for another, while recording the factored price. + + void exchange(commodity_t& commodity, + const amount_t& per_unit_cost, + const datetime_t& moment); + + cost_breakdown_t exchange(const amount_t& amount, + const amount_t& cost, + const bool is_per_unit = false, + const optional<datetime_t>& moment = none, + const optional<string>& tag = none); + + // Parse commodity prices from a textual representation + + optional<price_point_t> parse_price_directive(char * line); + + commodity_t * + parse_price_expression(const std::string& str, + const bool add_prices = true, + const optional<datetime_t>& moment = none); +}; + +} // namespace ledger + +#endif // _POOL_H diff --git a/src/post.cc b/src/post.cc index 3a0f3cc4..9b416434 100644 --- a/src/post.cc +++ b/src/post.cc @@ -244,6 +244,10 @@ namespace { return long(post.reported_account()->depth); } + value_t get_datetime(post_t& post) { + return post.xdata().datetime; + } + template <value_t (*Func)(post_t&)> value_t get_wrapper(call_scope_t& scope) { return (*Func)(find_scope<post_t>(scope)); @@ -282,6 +286,8 @@ expr_t::ptr_op_t post_t::lookup(const string& name) case 'd': if (name == "depth") return WRAP_FUNCTOR(get_wrapper<&get_account_depth>); + else if (name == "datetime") + return WRAP_FUNCTOR(get_wrapper<&get_datetime>); break; case 'h': @@ -296,6 +302,8 @@ expr_t::ptr_op_t post_t::lookup(const string& name) return WRAP_FUNCTOR(get_wrapper<&get_payee>); else if (name == "primary") return WRAP_FUNCTOR(get_wrapper<&get_commodity_is_primary>); + else if (name == "parent") + return WRAP_FUNCTOR(get_wrapper<&get_xact>); break; case 'r': @@ -140,6 +140,7 @@ public: value_t total; std::size_t count; date_t date; + datetime_t datetime; account_t * account; void * ptr; diff --git a/src/predicate.h b/src/predicate.h index 47eba04b..3e9fc6b1 100644 --- a/src/predicate.h +++ b/src/predicate.h @@ -48,6 +48,7 @@ #include "expr.h" #include "commodity.h" +#include "annotate.h" namespace ledger { diff --git a/src/py_amount.cc b/src/py_amount.cc index a5a34a0b..68fd8698 100644 --- a/src/py_amount.cc +++ b/src/py_amount.cc @@ -35,6 +35,8 @@ #include "pyutils.h" #include "pyfstream.h" #include "commodity.h" +#include "annotate.h" +#include "pool.h" namespace ledger { diff --git a/src/py_journal.cc b/src/py_journal.cc index 65eca838..fb693ffc 100644 --- a/src/py_journal.cc +++ b/src/py_journal.cc @@ -171,16 +171,6 @@ account_t& accounts_getitem(account_t& account, int i) return *(*elem).second; } -PyObject * py_account_get_data(account_t& account) -{ - return (PyObject *) account.data; -} - -void py_account_set_data(account_t& account, PyObject * obj) -{ - account.data = obj; -} - account_t * py_find_account_1(journal_t& journal, const string& name) { return journal.find_account(name); @@ -342,7 +332,6 @@ void export_journal() .def_readwrite("name", &account_t::name) .def_readwrite("note", &account_t::note) .def_readonly("depth", &account_t::depth) - .add_property("data", py_account_get_data, py_account_set_data) .def_readonly("ident", &account_t::ident) .def("fullname", &account_t::fullname) diff --git a/src/py_value.cc b/src/py_value.cc index c7847d00..8e579104 100644 --- a/src/py_value.cc +++ b/src/py_value.cc @@ -34,6 +34,7 @@ #include "pyinterp.h" #include "pyutils.h" #include "commodity.h" +#include "annotate.h" namespace ledger { diff --git a/src/pyledger.h b/src/pyledger.h deleted file mode 100644 index 3374e895..00000000 --- a/src/pyledger.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * - Neither the name of New Artisans LLC nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef _PYLEDGER_H -#define _PYLEDGER_H - - -#endif // _PYLEDGER_H diff --git a/src/quotes.cc b/src/quotes.cc index 5dbbc962..d4be462e 100644 --- a/src/quotes.cc +++ b/src/quotes.cc @@ -31,46 +31,39 @@ #include <system.hh> +#include "amount.h" +#include "commodity.h" +#include "pool.h" #include "quotes.h" namespace ledger { -#if 0 -void quotes_by_script::operator()(commodity_base_t& commodity, - const datetime_t& moment, - const datetime_t& date, - const datetime_t& last, - amount_t& price) +optional<price_point_t> +commodity_quote_from_script(commodity_t& commodity, + const optional<commodity_t&>& exchange_commodity) { - DEBUG_CLASS("ledger.quotes.download"); - - DEBUG_("commodity: " << commodity.symbol); - DEBUG_TIME_(current_moment); - DEBUG_TIME_(moment); - DEBUG_TIME_(date); - DEBUG_TIME_(last); - if (commodity.history) - DEBUG_TIME_(commodity.history->last_lookup); - DEBUG_("pricing_leeway is " << pricing_leeway); - - if ((commodity.history && - (current_moment - commodity.history->last_lookup) < pricing_leeway) || - (current_moment - last) < pricing_leeway || - (price && moment > date && (moment - date) <= pricing_leeway)) - return; - - using namespace std; - - DEBUG_("downloading quote for symbol " << commodity.symbol); + DEBUG("commodity.download", "downloading quote for symbol " << commodity.symbol()); +#if defined(DEBUG_ON) + if (exchange_commodity) + DEBUG("commodity.download", + " in terms of commodity " << exchange_commodity->symbol()); +#endif char buf[256]; buf[0] = '\0'; - bool success = true; + string getquote_cmd("getquote \""); + getquote_cmd += commodity.symbol(); + getquote_cmd += "\" \""; + if (exchange_commodity) + getquote_cmd += exchange_commodity->symbol(); + getquote_cmd += "\""; + + DEBUG("commodity.download", "invoking command: " << getquote_cmd); - if (FILE * fp = popen((string("getquote \"") + - commodity.symbol + "\"").c_str(), "r")) { - if (feof(fp) || ! fgets(buf, 255, fp)) + bool success = true; + if (FILE * fp = popen(getquote_cmd.c_str(), "r")) { + if (std::feof(fp) || ! std::fgets(buf, 255, fp)) success = false; if (pclose(fp) != 0) success = false; @@ -79,31 +72,41 @@ void quotes_by_script::operator()(commodity_base_t& commodity, } if (success && buf[0]) { - char * p = strchr(buf, '\n'); - if (p) *p = '\0'; - - DEBUG_("downloaded quote: " << buf); - - price.parse(buf); - commodity.add_price(current_moment, price); - - commodity.history->last_lookup = current_moment; + if (char * p = std::strchr(buf, '\n')) *p = '\0'; + DEBUG("commodity.download", "downloaded quote: " << buf); - if (price && ! price_db.empty()) { + if (optional<price_point_t> point = + amount_t::current_pool->parse_price_directive(buf)) { + if (amount_t::current_pool->price_db) { #if defined(__GNUG__) && __GNUG__ < 3 - ofstream database(price_db.c_str(), ios::out | ios::app); + ofstream database(*amount_t::current_pool->price_db, + ios::out | ios::app); #else - ofstream database(price_db.c_str(), ios_base::out | ios_base::app); + ofstream database(*amount_t::current_pool->price_db, + std::ios_base::out | std::ios_base::app); #endif - database << "P " << current_moment.to_string("%Y/%m/%d %H:%M:%S") - << " " << commodity.symbol << " " << price << endl; + database << "P " + << format_datetime(point->when, string("%Y/%m/%d %H:%M:%S")) + << " " << commodity.symbol() + << " " << point->price + << std::endl; + } + return point; } } else { - throw_(std::runtime_error, - _("Failed to download price for '%1' (command: \"getquote %1\")") - << commodity.symbol); + DEBUG("commodity.download", + "Failed to download price for '" << commodity.symbol() << + "' (command: \"getquote " << commodity.symbol() << + " " << (exchange_commodity ? + exchange_commodity->symbol() : "''") << "\")"); + + // Don't try to download this commodity again. + + // jww (2009-06-24): This flag should be removed in order to try again + // when using a GUI. + commodity.add_flags(COMMODITY_NOMARKET); } + return none; } -#endif } // namespace ledger diff --git a/src/quotes.h b/src/quotes.h index 68c7cade..5db69d1f 100644 --- a/src/quotes.h +++ b/src/quotes.h @@ -46,39 +46,11 @@ #ifndef _QUOTES_H #define _QUOTES_H - namespace ledger { -#if 0 -/** - * @brief Brief - * - * Long. - */ -class quotes_by_script : public noncopyable, public commodity_t::base_t::updater_t -{ - string price_db; - std::size_t pricing_leeway; - - quotes_by_script(); - -public: - quotes_by_script(path _price_db, - std::size_t _pricing_leeway) - : price_db(_price_db), pricing_leeway(_pricing_leeway) { - TRACE_CTOR(quotes_by_script, "path, std::size_t, bool&"); - } - ~quotes_by_script() throw() { - TRACE_DTOR(quotes_by_script); - } - - virtual void operator()(commodity_base_t& commodity, - const datetime_t& moment, - const datetime_t& date, - const datetime_t& last, - amount_t& price); -}; -#endif +optional<price_point_t> +commodity_quote_from_script(commodity_t& commodity, + const optional<commodity_t&>& exchange_commodity); } // namespace ledger diff --git a/src/report.cc b/src/report.cc index be1e47ee..4c8a40e6 100644 --- a/src/report.cc +++ b/src/report.cc @@ -55,12 +55,15 @@ void report_t::posts_report(post_handler_ptr handler) void report_t::generate_report(post_handler_ptr handler) { - HANDLER(limit_).on("actual"); // jww (2009-02-27): make this more general + // jww (2009-02-27): make this more general + HANDLER(limit_).on(string("#generate"), "actual"); + generate_posts_iterator walker (session, HANDLED(seed_) ? static_cast<unsigned int>(HANDLER(seed_).value.to_long()) : 0, HANDLED(head_) ? static_cast<unsigned int>(HANDLER(head_).value.to_long()) : 50); + pass_down_posts(chain_post_handlers(*this, handler), walker); } @@ -126,33 +129,20 @@ value_t report_t::fn_market(call_scope_t& scope) { interactive_t args(scope, "a&ts"); - if (args.has(2)) { - scoped_array<char> buf(new char[args.get<string>(2).length() + 1]); - std::strcpy(buf.get(), args.get<string>(2).c_str()); - - for (char * p = std::strtok(buf.get(), ","); - p; - p = std::strtok(NULL, ",")) { - if (commodity_t * commodity = amount_t::current_pool->find(trim_ws(p))) { - DEBUG("report.market", "Searching for value of " << args.value_at(0) - << " in terms of commodity " << commodity->symbol()); - value_t result = - args.value_at(0).value(false, args.has(1) ? + value_t result; + optional<datetime_t> moment = (args.has(1) ? args.get<datetime_t>(1) : - optional<datetime_t>(), *commodity); - if (! result.is_null()) { - DEBUG("report.market", "Market value is = " << result); - return result; - } - } - } - } else { - value_t result = - args.value_at(0).value(true, args.has(1) ? - args.get<datetime_t>(1) : optional<datetime_t>()); - if (! result.is_null()) - return result; - } + optional<datetime_t>()); + if (args.has(2)) + result = args.value_at(0).exchange_commodities(args.get<string>(2), + /* add_prices= */ false, + moment); + else + result = args.value_at(0).value(true, moment); + + if (! result.is_null()) + return result; + return args.value_at(0); } @@ -227,13 +217,14 @@ value_t report_t::fn_truncated(call_scope_t& scope) value_t report_t::fn_justify(call_scope_t& scope) { - interactive_t args(scope, "vl&lbs"); + interactive_t args(scope, "vl&lbbs"); std::ostringstream out; args.value_at(0) .print(out, args.get<long>(1), args.has(2) ? args.get<long>(2) : -1, - args.has(3), - args.has(4) ? args.get<string>(4) : + args.has(3) ? args.get<bool>(3) : false, + args.has(4) ? args.get<bool>(4) : false, + args.has(5) ? args.get<string>(5) : (HANDLED(date_format_) ? HANDLER(date_format_).str() : optional<string>())); return string_value(out.str()); @@ -255,22 +246,26 @@ value_t report_t::fn_quoted(call_scope_t& args) return string_value(out.str()); } -value_t report_t::fn_join(call_scope_t& args) +value_t report_t::fn_join(call_scope_t& scope) { + interactive_t args(scope, "s"); + std::ostringstream out; - foreach (const char ch, args[0].to_string()) + foreach (const char ch, args.get<string>(0)) { if (ch != '\n') out << ch; else out << "\\n"; - + } return string_value(out.str()); } -value_t report_t::fn_format_date(call_scope_t& args) +value_t report_t::fn_format_date(call_scope_t& scope) { - return string_value(format_date(args[0].to_date(), args[1].to_string())); + interactive_t args(scope, "ds"); + return string_value(format_date(args.get<date_t>(0), + args.get<string>(1))); } value_t report_t::fn_ansify_if(call_scope_t& scope) @@ -299,6 +294,19 @@ value_t report_t::fn_ansify_if(call_scope_t& scope) } } +value_t report_t::fn_percent(call_scope_t& scope) +{ + interactive_t args(scope, "aa"); + return (amount_t("100.00%") * + (args.get<amount_t>(0) / args.get<amount_t>(1)).number()); +} + +value_t report_t::fn_price(call_scope_t& scope) +{ + interactive_t args(scope, "v"); + return args.value_at(0).price(); +} + namespace { value_t fn_black(call_scope_t&) { return string_value("black"); @@ -349,10 +357,12 @@ namespace { shared_ptr<item_handler<Type> > handler; report_t& report; + string whence; public: - reporter(item_handler<Type> * _handler, report_t& _report) - : handler(_handler), report(_report) {} + reporter(item_handler<Type> * _handler, report_t& _report, + const string& _whence) + : handler(_handler), report(_report), whence(_whence) {} value_t operator()(call_scope_t& args) { @@ -365,7 +375,7 @@ namespace { string limit = args_to_predicate_expr(begin, end); if (! limit.empty()) - report.HANDLER(limit_).on(limit); + report.HANDLER(limit_).on(whence, limit); DEBUG("report.predicate", "Predicate = " << report.HANDLER(limit_).str()); @@ -375,7 +385,7 @@ namespace { display = args_to_predicate_expr(begin, end); if (! display.empty()) - report.HANDLER(display_).on(display); + report.HANDLER(display_).on(whence, display); DEBUG("report.predicate", "Display predicate = " << report.HANDLER(display_).str()); @@ -408,7 +418,7 @@ option_t<report_t> * report_t::lookup_option(const char * p) { switch (*p) { case '%': - OPT_CH(percentage); + OPT_CH(percent); break; case 'A': OPT_CH(average); @@ -473,9 +483,6 @@ option_t<report_t> * report_t::lookup_option(const char * p) case 'Y': OPT_CH(yearly); break; - case 'Z': - OPT_CH(price_exp_); - break; case 'a': OPT(abbrev_len_); else OPT(account_); @@ -556,7 +563,6 @@ option_t<report_t> * report_t::lookup_option(const char * p) else OPT(lots); else OPT(lots_actual); else OPT_ALT(tail_, last_); - else OPT_ALT(price_exp_, leeway_); break; case 'm': OPT(market); @@ -564,6 +570,7 @@ option_t<report_t> * report_t::lookup_option(const char * p) break; case 'n': OPT_CH(collapse); + else OPT(no_color); else OPT(no_total); break; case 'o': @@ -574,13 +581,12 @@ option_t<report_t> * report_t::lookup_option(const char * p) OPT(pager_); else OPT(payee_as_account); else OPT(pending); - else OPT(percentage); + else OPT(percent); else OPT_(period_); - else OPT(period_sort_); + else OPT_ALT(sort_xacts_, period_sort_); else OPT(plot_amount_format_); else OPT(plot_total_format_); else OPT(price); - else OPT(price_exp_); else OPT(prices_format_); else OPT(pricesdb_format_); else OPT(print_format_); @@ -598,11 +604,14 @@ option_t<report_t> * report_t::lookup_option(const char * p) else OPT(related_all); else OPT(revalued); else OPT(revalued_only); +#if 0 + // This option is not available to users + else OPT(revalued_total_); +#endif break; case 's': OPT(set_account_); else OPT(set_payee_); - else OPT(set_price_); else OPT(sort_); else OPT(sort_all_); else OPT(sort_xacts_); @@ -675,7 +684,7 @@ expr_t::ptr_op_t report_t::lookup(const string& name) return expr_t::op_t::wrap_functor (reporter<account_t, acct_handler_ptr, &report_t::accounts_report> (new format_accounts(*this, report_format(HANDLER(balance_format_))), - *this)); + *this, "#balance")); break; case 'c': @@ -683,7 +692,7 @@ expr_t::ptr_op_t report_t::lookup(const string& name) return WRAP_FUNCTOR (reporter<> (new format_posts(*this, report_format(HANDLER(csv_format_))), - *this)); + *this, "#csv")); break; case 'e': @@ -691,12 +700,12 @@ expr_t::ptr_op_t report_t::lookup(const string& name) return WRAP_FUNCTOR (reporter<> (new format_posts(*this, report_format(HANDLER(print_format_))), - *this)); - else if (is_eq(q, "xact") || is_eq(q, "entry")) + *this, "#equity")); + else if (is_eq(q, "entry")) return WRAP_FUNCTOR(xact_command); else if (is_eq(q, "emacs")) return WRAP_FUNCTOR - (reporter<>(new format_emacs_posts(output_stream), *this)); + (reporter<>(new format_emacs_posts(output_stream), *this, "#emacs")); break; case 'p': @@ -704,17 +713,17 @@ expr_t::ptr_op_t report_t::lookup(const string& name) return WRAP_FUNCTOR (reporter<> (new format_posts(*this, report_format(HANDLER(print_format_)), - HANDLED(raw)), *this)); + HANDLED(raw)), *this, "#print")); else if (is_eq(q, "prices")) return expr_t::op_t::wrap_functor (reporter<post_t, post_handler_ptr, &report_t::commodities_report> (new format_posts(*this, report_format(HANDLER(prices_format_))), - *this)); + *this, "#prices")); else if (is_eq(q, "pricesdb")) return expr_t::op_t::wrap_functor (reporter<post_t, post_handler_ptr, &report_t::commodities_report> (new format_posts(*this, report_format(HANDLER(pricesdb_format_))), - *this)); + *this, "#pricesdb")); else if (is_eq(q, "python") && maybe_import("ledger.interp")) return session.lookup(string(CMD_PREFIX) + "python"); break; @@ -724,7 +733,7 @@ expr_t::ptr_op_t report_t::lookup(const string& name) return WRAP_FUNCTOR (reporter<> (new format_posts(*this, report_format(HANDLER(register_format_))), - *this)); + *this, "#register")); else if (is_eq(q, "reload")) return MAKE_FUNCTOR(report_t::reload_command); break; @@ -736,6 +745,11 @@ expr_t::ptr_op_t report_t::lookup(const string& name) if (is_eq(q, "server") && maybe_import("ledger.server")) return session.lookup(string(CMD_PREFIX) + "server"); break; + + case 'x': + if (is_eq(q, "xact")) + return WRAP_FUNCTOR(xact_command); + break; } } else if (is_eq(p, "cyan")) @@ -747,6 +761,8 @@ expr_t::ptr_op_t report_t::lookup(const string& name) return MAKE_FUNCTOR(report_t::fn_display_amount); else if (is_eq(p, "display_total")) return MAKE_FUNCTOR(report_t::fn_display_total); + else if (is_eq(p, "date")) + return MAKE_FUNCTOR(report_t::fn_now); break; case 'f': @@ -783,6 +799,8 @@ expr_t::ptr_op_t report_t::lookup(const string& name) case 'n': if (is_eq(p, "null")) return WRAP_FUNCTOR(fn_null); + else if (is_eq(p, "now")) + return MAKE_FUNCTOR(report_t::fn_now); break; case 'o': @@ -815,7 +833,7 @@ expr_t::ptr_op_t report_t::lookup(const string& name) return expr_t::op_t::wrap_functor (reporter<post_t, post_handler_ptr, &report_t::generate_report> (new format_posts(*this, report_format(HANDLER(print_format_)), - false), *this)); + false), *this, "#generate")); case 'h': if (is_eq(q, "hello") && maybe_import("ledger.hello")) return session.lookup(string(PRECMD_PREFIX) + "hello"); @@ -834,6 +852,10 @@ expr_t::ptr_op_t report_t::lookup(const string& name) } else if (is_eq(p, "post")) return WRAP_FUNCTOR(fn_false); + else if (is_eq(p, "percent")) + return MAKE_FUNCTOR(report_t::fn_percent); + else if (is_eq(p, "price")) + return MAKE_FUNCTOR(report_t::fn_price); break; case 'q': @@ -862,6 +884,8 @@ expr_t::ptr_op_t report_t::lookup(const string& name) return MAKE_FUNCTOR(report_t::fn_truncated); else if (is_eq(p, "total_expr")) return MAKE_FUNCTOR(report_t::fn_total_expr); + else if (is_eq(p, "today")) + return MAKE_FUNCTOR(report_t::fn_today); break; case 'u': diff --git a/src/report.h b/src/report.h index 5eb2a706..71dc4ca2 100644 --- a/src/report.h +++ b/src/report.h @@ -52,6 +52,8 @@ #include "stream.h" #include "option.h" #include "commodity.h" +#include "annotate.h" +#include "format.h" namespace ledger { @@ -119,10 +121,12 @@ public: #define BUDGET_BUDGETED 0x01 #define BUDGET_UNBUDGETED 0x02 + datetime_t terminus; uint_least8_t budget_flags; explicit report_t(session_t& _session) - : session(_session), budget_flags(BUDGET_NO_BUDGET) {} + : session(_session), terminus(CURRENT_TIME()), + budget_flags(BUDGET_NO_BUDGET) {} virtual ~report_t() { output_stream.close(); @@ -153,6 +157,15 @@ public: value_t fn_join(call_scope_t& scope); value_t fn_format_date(call_scope_t& scope); value_t fn_ansify_if(call_scope_t& scope); + value_t fn_percent(call_scope_t& scope); + value_t fn_price(call_scope_t& scope); + + value_t fn_now(call_scope_t&) { + return terminus; + } + value_t fn_today(call_scope_t&) { + return terminus.date(); + } value_t fn_options(call_scope_t&) { return value_t(static_cast<scope_t *>(this)); @@ -176,6 +189,110 @@ public: bool maybe_import(const string& module); + void report_options(std::ostream& out) + { + HANDLER(abbrev_len_).report(out); + HANDLER(account_).report(out); + HANDLER(actual).report(out); + HANDLER(add_budget).report(out); + HANDLER(amount_).report(out); + HANDLER(amount_data).report(out); + HANDLER(anon).report(out); + HANDLER(average).report(out); + HANDLER(balance_format_).report(out); + HANDLER(base).report(out); + HANDLER(basis).report(out); + HANDLER(begin_).report(out); + HANDLER(budget).report(out); + HANDLER(by_payee).report(out); + HANDLER(cleared).report(out); + HANDLER(code_as_payee).report(out); + HANDLER(comm_as_payee).report(out); + HANDLER(code_as_account).report(out); + HANDLER(comm_as_account).report(out); + HANDLER(color).report(out); + HANDLER(collapse).report(out); + HANDLER(collapse_if_zero).report(out); + HANDLER(columns_).report(out); + HANDLER(csv_format_).report(out); + HANDLER(current).report(out); + HANDLER(daily).report(out); + HANDLER(date_format_).report(out); + HANDLER(depth_).report(out); + HANDLER(deviation).report(out); + HANDLER(display_).report(out); + HANDLER(display_amount_).report(out); + HANDLER(display_total_).report(out); + HANDLER(dow).report(out); + HANDLER(effective).report(out); + HANDLER(empty).report(out); + HANDLER(end_).report(out); + HANDLER(equity).report(out); + HANDLER(exact).report(out); + HANDLER(exchange_).report(out); + HANDLER(flat).report(out); + HANDLER(forecast_while_).report(out); + HANDLER(format_).report(out); + HANDLER(gain).report(out); + HANDLER(head_).report(out); + HANDLER(invert).report(out); + HANDLER(limit_).report(out); + HANDLER(lot_dates).report(out); + HANDLER(lot_prices).report(out); + HANDLER(lot_tags).report(out); + HANDLER(lots).report(out); + HANDLER(lots_actual).report(out); + HANDLER(market).report(out); + HANDLER(monthly).report(out); + HANDLER(no_total).report(out); + HANDLER(only_).report(out); + HANDLER(output_).report(out); + HANDLER(pager_).report(out); + HANDLER(payee_as_account).report(out); + HANDLER(pending).report(out); + HANDLER(percent).report(out); + HANDLER(period_).report(out); + HANDLER(plot_amount_format_).report(out); + HANDLER(plot_total_format_).report(out); + HANDLER(price).report(out); + HANDLER(prices_format_).report(out); + HANDLER(pricesdb_format_).report(out); + HANDLER(print_format_).report(out); + HANDLER(quantity).report(out); + HANDLER(quarterly).report(out); + HANDLER(raw).report(out); + HANDLER(real).report(out); + HANDLER(register_format_).report(out); + HANDLER(related).report(out); + HANDLER(related_all).report(out); + HANDLER(revalued).report(out); + HANDLER(revalued_only).report(out); + HANDLER(revalued_total_).report(out); + HANDLER(seed_).report(out); + HANDLER(set_account_).report(out); + HANDLER(set_payee_).report(out); + HANDLER(sort_).report(out); + HANDLER(sort_all_).report(out); + HANDLER(sort_xacts_).report(out); + HANDLER(start_of_week_).report(out); + HANDLER(subtotal).report(out); + HANDLER(tail_).report(out); + HANDLER(total_).report(out); + HANDLER(total_data).report(out); + HANDLER(truncate_).report(out); + HANDLER(unbudgeted).report(out); + HANDLER(uncleared).report(out); + HANDLER(unround).report(out); + HANDLER(weekly).report(out); + HANDLER(wide).report(out); + HANDLER(yearly).report(out); + HANDLER(date_width_).report(out); + HANDLER(payee_width_).report(out); + HANDLER(account_width_).report(out); + HANDLER(amount_width_).report(out); + HANDLER(total_width_).report(out); + } + option_t<report_t> * lookup_option(const char * p); virtual void define(const string& name, expr_t::ptr_op_t def); @@ -187,11 +304,11 @@ public: */ OPTION__(report_t, abbrev_len_, - CTOR(report_t, abbrev_len_) { on_with(2L); }); + CTOR(report_t, abbrev_len_) { on_with(none, 2L); }); OPTION(report_t, account_); OPTION_(report_t, actual, DO() { // -L - parent->HANDLER(limit_).on("actual"); + parent->HANDLER(limit_).on(string("--actual"), "actual"); }); OPTION_(report_t, add_budget, DO() { @@ -202,53 +319,50 @@ public: (report_t, amount_, // -t expr_t expr; CTOR(report_t, amount_) { - set_expr("amount"); + set_expr(none, "amount"); } - void set_expr(const string& str) { + void set_expr(const optional<string>& whence, const string& str) { expr = str; - on(str); + on(whence, str); } DO_(args) { - set_expr(args[0].to_string()); + set_expr(args[0].to_string(), args[1].to_string()); }); - OPTION_(report_t, amount_data, DO() { // -j - parent->HANDLER(format_).on_with(parent->HANDLER(plot_amount_format_).value); - }); - + OPTION(report_t, amount_data); OPTION(report_t, anon); OPTION_(report_t, average, DO() { // -A - parent->HANDLER(display_total_).set_expr("total_expr/count"); + parent->HANDLER(display_total_) + .set_expr(string("--average"), "total_expr/count"); }); OPTION__(report_t, balance_format_, CTOR(report_t, balance_format_) { - on("%(ansify_if(justify(scrub(display_total), 20, -1, true), " - " red if color & scrub(display_total) < 0))" + on(none, + "%(justify(scrub(display_total), 20, -1, true, color))" " %(!options.flat ? depth_spacer : \"\")" "%-(ansify_if(partial_account(options.flat), blue if color))\n%/" - "%(ansify_if(justify(scrub(display_total), 20, -1, true), " - " red if color & scrub(display_total) < 0))\n%/" + "%(justify(scrub(display_total), 20, -1, true, color))\n%/" "--------------------\n"); }); OPTION(report_t, base); OPTION_(report_t, basis, DO() { // -B - parent->HANDLER(revalued).on_only(); - parent->HANDLER(amount_).set_expr("rounded(cost)"); + parent->HANDLER(revalued).on_only(string("--basis")); + parent->HANDLER(amount_).set_expr(string("--basis"), "rounded(cost)"); }); OPTION_(report_t, begin_, DO_(args) { // -b - date_interval_t interval(args[0].to_string()); + date_interval_t interval(args[1].to_string()); if (! interval.start) throw_(std::invalid_argument, _("Could not determine beginning of period '%1'") - << args[0].to_string()); + << args[1].to_string()); string predicate = "date>=[" + to_iso_extended_string(*interval.start) + "]"; - parent->HANDLER(limit_).on(predicate); + parent->HANDLER(limit_).on(string("--begin"), predicate); }); OPTION_(report_t, budget, DO() { @@ -258,7 +372,7 @@ public: OPTION(report_t, by_payee); // -P OPTION_(report_t, cleared, DO() { // -C - parent->HANDLER(limit_).on("cleared"); + parent->HANDLER(limit_).on(string("--cleared"), "cleared"); }); OPTION(report_t, code_as_payee); @@ -270,17 +384,18 @@ public: OPTION_(report_t, collapse, DO() { // -n // Make sure that balance reports are collapsed too, but only apply it // to account xacts - parent->HANDLER(display_).on("post|depth<=1"); + parent->HANDLER(display_).on(string("--collapse"), "post|depth<=1"); }); OPTION_(report_t, collapse_if_zero, DO() { - parent->HANDLER(collapse).on_only(); + parent->HANDLER(collapse).on_only(string("--collapse-if-zero")); }); OPTION(report_t, columns_); OPTION__(report_t, csv_format_, CTOR(report_t, csv_format_) { - on("%(quoted(date))," + on(none, + "%(quoted(date))," "%(quoted(payee))," "%(quoted(account))," "%(quoted(scrub(display_amount)))," @@ -290,35 +405,38 @@ public: }); OPTION_(report_t, current, DO() { // -c - parent->HANDLER(limit_).on("date<=today"); + parent->HANDLER(limit_).on(string("--current"), "date<=today"); }); OPTION_(report_t, daily, DO() { - parent->HANDLER(period_).on("daily"); + parent->HANDLER(period_).on(string("--daily"), "daily"); }); OPTION__(report_t, date_format_, // -y CTOR(report_t, date_format_) { - on("%y-%b-%d"); + on(none, "%y-%b-%d"); }); OPTION_(report_t, depth_, DO_(scope) { - interactive_t args(scope, "l"); - parent->HANDLER(display_).on(string("depth<=") + args.get<string>(0)); + interactive_t args(scope, "sl"); + parent->HANDLER(display_).on(string("--depth"), + string("depth<=") + args.get<string>(1)); }); OPTION_(report_t, deviation, DO() { // -D - parent->HANDLER(display_total_).set_expr("amount_expr-total_expr/count"); + parent->HANDLER(display_total_) + .set_expr(string("--deviation"), "amount_expr-total_expr/count"); }); OPTION__ (report_t, display_, // -d CTOR(report_t, display_) {} - virtual void on_with(const value_t& text) { + virtual void on_with(const optional<string>& whence, const value_t& text) { if (! handled) - option_t<report_t>::on_with(text); + option_t<report_t>::on_with(whence, text); else - option_t<report_t>::on_with(string_value(string("(") + str() + ")&(" + + option_t<report_t>::on_with(whence, + string_value(string("(") + str() + ")&(" + text.as_string() + ")")); }); @@ -326,28 +444,28 @@ public: (report_t, display_amount_, expr_t expr; CTOR(report_t, display_amount_) { - set_expr("amount_expr"); + set_expr(none, "amount_expr"); } - void set_expr(const string& str) { + void set_expr(const optional<string>& whence, const string& str) { expr = str; - on(str); + on(whence, str); } DO_(args) { - set_expr(args[0].to_string()); + set_expr(args[0].to_string(), args[1].to_string()); }); OPTION__ (report_t, display_total_, expr_t expr; CTOR(report_t, display_total_) { - set_expr("total_expr"); + set_expr(none, "total_expr"); } - void set_expr(const string& str) { + void set_expr(const optional<string>& whence, const string& str) { expr = str; - on(str); + on(whence, str); } DO_(args) { - set_expr(args[0].to_string()); + set_expr(args[0].to_string(), args[1].to_string()); }); OPTION(report_t, dow); @@ -355,26 +473,26 @@ public: OPTION(report_t, empty); // -E OPTION_(report_t, end_, DO_(args) { // -e - date_interval_t interval(args[0].to_string()); + date_interval_t interval(args[1].to_string()); if (! interval.start) throw_(std::invalid_argument, _("Could not determine end of period '%1'") - << args[0].to_string()); + << args[1].to_string()); string predicate = "date<[" + to_iso_extended_string(*interval.start) + "]"; - parent->HANDLER(limit_).on(predicate); -#if 0 - terminus = interval.begin; -#endif + parent->HANDLER(limit_).on(string("--end"), predicate); + + parent->terminus = datetime_t(*interval.start); }); OPTION(report_t, equity); OPTION(report_t, exact); OPTION_(report_t, exchange_, DO_(args) { // -X - on_with(args[0]); + on_with(args[0].as_string(), args[1]); call_scope_t no_args(*parent); + no_args.push_back(args[0]); parent->HANDLER(market).parent = parent; parent->HANDLER(market).handler(no_args); }); @@ -384,21 +502,24 @@ public: OPTION(report_t, format_); // -F OPTION_(report_t, gain, DO() { // -G - parent->HANDLER(revalued).on_only(); - parent->HANDLER(amount_).set_expr("(amount, cost)"); + parent->HANDLER(revalued).on_only(string("--gain")); + parent->HANDLER(amount_).set_expr(string("--gain"), "(amount, cost)"); // Since we are displaying the amounts of revalued postings, they // will end up being composite totals, and hence a pair of pairs. parent->HANDLER(display_amount_) - .set_expr("use_direct_amount ? amount :" + .set_expr(string("--gain"), + "use_direct_amount ? amount :" " (is_seq(get_at(amount_expr, 0)) ?" " get_at(get_at(amount_expr, 0), 0) :" " market(get_at(amount_expr, 0), date, exchange)" " - get_at(amount_expr, 1))"); parent->HANDLER(revalued_total_) - .set_expr("(market(get_at(total_expr, 0), date, exchange), " + .set_expr(string("--gain"), + "(market(get_at(total_expr, 0), date, exchange), " "get_at(total_expr, 1))"); parent->HANDLER(display_total_) - .set_expr("use_direct_amount ? total_expr :" + .set_expr(string("--gain"), + "use_direct_amount ? total_expr :" " market(get_at(total_expr, 0), date, exchange)" " - get_at(total_expr, 1)"); }); @@ -406,17 +527,18 @@ public: OPTION(report_t, head_); OPTION_(report_t, invert, DO() { - parent->HANDLER(amount_).set_expr("-amount"); + parent->HANDLER(amount_).set_expr(string("--invert"), "-amount"); }); OPTION__ (report_t, limit_, // -l CTOR(report_t, limit_) {} - virtual void on_with(const value_t& text) { + virtual void on_with(const optional<string>& whence, const value_t& text) { if (! handled) - option_t<report_t>::on_with(text); + option_t<report_t>::on_with(whence, text); else - option_t<report_t>::on_with(string_value(string("(") + str() + ")&(" + + option_t<report_t>::on_with(whence, + string_value(string("(") + str() + ")&(" + text.as_string() + ")")); }); @@ -427,15 +549,19 @@ public: OPTION(report_t, lots_actual); OPTION_(report_t, market, DO() { // -V - parent->HANDLER(revalued).on_only(); + parent->HANDLER(revalued).on_only(string("--market")); parent->HANDLER(display_amount_) - .set_expr("market(amount_expr, date, exchange)"); + .set_expr(string("--market"), "market(amount_expr, date, exchange)"); parent->HANDLER(display_total_) - .set_expr("market(total_expr, date, exchange)"); + .set_expr(string("--market"), "market(total_expr, date, exchange)"); }); OPTION_(report_t, monthly, DO() { // -M - parent->HANDLER(period_).on("monthly"); + parent->HANDLER(period_).on(string("--monthly"), "monthly"); + }); + + OPTION_(report_t, no_color, DO() { + parent->HANDLER(color).off(); }); OPTION(report_t, no_total); @@ -443,11 +569,12 @@ public: OPTION__ (report_t, only_, CTOR(report_t, only_) {} - virtual void on_with(const value_t& text) { + virtual void on_with(const optional<string>& whence, const value_t& text) { if (! handled) - option_t<report_t>::on_with(text); + option_t<report_t>::on_with(whence, text); else - option_t<report_t>::on_with(string_value(string("(") + str() + ")&(" + + option_t<report_t>::on_with(whence, + string_value(string("(") + str() + ")&(" + text.as_string() + ")")); }); @@ -456,48 +583,57 @@ public: OPTION(report_t, payee_as_account); OPTION_(report_t, pending, DO() { // -C - parent->HANDLER(limit_).on("pending"); + parent->HANDLER(limit_).on(string("--pending"), "pending"); }); - OPTION(report_t, percentage); // -% + OPTION_(report_t, percent, DO() { // -% + parent->HANDLER(total_) + .set_expr(string("--percent"), + "is_account&parent&parent.total&percent(total, parent.total)"); + }); OPTION__ (report_t, period_, // -p CTOR(report_t, period_) {} - virtual void on_with(const value_t& text) { + virtual void on_with(const optional<string>& whence, const value_t& text) { if (! handled) - option_t<report_t>::on_with(text); + option_t<report_t>::on_with(whence, text); else - option_t<report_t>::on_with(string_value(text.as_string() + " " + str())); + option_t<report_t>::on_with(whence, + string_value(text.as_string() + " " + str())); }); - OPTION(report_t, period_sort_); - OPTION__(report_t, plot_amount_format_, CTOR(report_t, plot_amount_format_) { - on("%(format_date(date, \"%Y-%m-%d\")) %(quantity(scrub(display_amount)))\n"); + on(none, + "%(format_date(date, \"%Y-%m-%d\")) %(quantity(scrub(display_amount)))\n"); }); OPTION__(report_t, plot_total_format_, CTOR(report_t, plot_total_format_) { - on("%(format_date(date, \"%Y-%m-%d\")) %(quantity(scrub(display_total)))\n"); + on(none, + "%(format_date(date, \"%Y-%m-%d\")) %(quantity(scrub(display_total)))\n"); }); OPTION_(report_t, price, DO() { // -I - parent->HANDLER(revalued).off(); - parent->HANDLER(amount_).set_expr("price"); + parent->HANDLER(display_amount_) + .set_expr(string("--price"), "price(amount_expr)"); + parent->HANDLER(display_total_) + .set_expr(string("--price"), "price(total_expr)"); }); - OPTION(report_t, price_exp_); // -Z - OPTION__(report_t, prices_format_, CTOR(report_t, prices_format_) { - on("%-.9(date) %-8(account) %12(scrub(display_amount))\n"); + on(none, + "%-.9(date) %-8(account) %(justify(scrub(display_amount), 12, " + " 2 + 9 + 8 + 12, true, color))\n"); }); OPTION__(report_t, pricesdb_format_, CTOR(report_t, pricesdb_format_) { - on("P %[%Y/%m/%d %H:%M:%S] %A %t\n"); + on(none, + "P %(datetime) %(account) %(scrub(display_amount))\n"); }); OPTION__(report_t, print_format_, CTOR(report_t, print_format_) { - on("%(format_date(xact.date, \"%Y/%m/%d\"))" + on(none, + "%(format_date(xact.date, \"%Y/%m/%d\"))" "%(!effective & xact.effective_date ?" " \"=\" + format_date(xact.effective_date, \"%Y/%m/%d\") : \"\")" "%(xact.cleared ? \" *\" : (xact.pending ? \" !\" : \"\"))" @@ -521,45 +657,50 @@ public: OPTION_(report_t, quantity, DO() { // -O parent->HANDLER(revalued).off(); - parent->HANDLER(amount_).set_expr("amount"); - parent->HANDLER(total_).set_expr("total"); + parent->HANDLER(amount_).set_expr(string("--quantity"), "amount"); + parent->HANDLER(total_).set_expr(string("--quantity"), "total"); }); OPTION_(report_t, quarterly, DO() { - parent->HANDLER(period_).on("quarterly"); + parent->HANDLER(period_).on(string("--quarterly"), "quarterly"); }); OPTION(report_t, raw); OPTION_(report_t, real, DO() { // -R - parent->HANDLER(limit_).on("real"); + parent->HANDLER(limit_).on(string("--real"), "real"); }); OPTION__(report_t, register_format_, CTOR(report_t, register_format_) { - on("%(ansify_if(justify(date, date_width), green if color & date > today))" + on(none, + "%(ansify_if(justify(date, date_width), green if color & date > today))" " %(ansify_if(justify(truncated(payee, payee_width), payee_width), " " bold if color & !cleared))" " %(ansify_if(justify(truncated(account, account_width, abbrev_len), " " account_width), blue if color))" - " %(ansify_if(justify(scrub(display_amount), amount_width, " - " 3 + date_width + payee_width + account_width + amount_width, true), " - " red if color & scrub(display_amount) < 0))" - " %(ansify_if(justify(scrub(display_total), total_width, " + " %(justify(scrub(display_amount), amount_width, " + " 3 + date_width + payee_width + account_width + amount_width, " + " true, color))" + " %(justify(scrub(display_total), total_width, " " 4 + date_width + payee_width + account_width + amount_width " - " + total_width, true), red if color & scrub(display_amount) < 0))\n%/" + " + total_width, true, color))\n%/" "%(justify(\" \", 2 + date_width + payee_width))" "%(ansify_if(justify(truncated(account, account_width, abbrev_len), " " account_width), blue if color))" - " %(ansify_if(justify(scrub(display_amount), amount_width, " - " 3 + date_width + payee_width + account_width + amount_width, true), " - " red if color & scrub(display_amount) < 0))" - " %(ansify_if(justify(scrub(display_total), total_width, " + " %(justify(scrub(display_amount), amount_width, " + " 3 + date_width + payee_width + account_width + amount_width, " + " true, color))" + " %(justify(scrub(display_total), total_width, " " 4 + date_width + payee_width + account_width + amount_width " - " + total_width, true), red if color & scrub(display_amount) < 0))\n"); + " + total_width, true, color))\n"); }); OPTION(report_t, related); // -r - OPTION(report_t, related_all); + + OPTION_(report_t, related_all, DO() { + parent->HANDLER(related).on_only(string("--related-all")); + }); + OPTION(report_t, revalued); OPTION(report_t, revalued_only); @@ -567,32 +708,31 @@ public: (report_t, revalued_total_, expr_t expr; CTOR(report_t, revalued_total_) {} - void set_expr(const string& str) { + void set_expr(const optional<string>& whence, const string& str) { expr = str; - on(str); + on(whence, str); } DO_(args) { - set_expr(args[0].to_string()); + set_expr(args[0].to_string(), args[1].to_string()); }); OPTION(report_t, seed_); OPTION(report_t, set_account_); OPTION(report_t, set_payee_); - OPTION(report_t, set_price_); OPTION_(report_t, sort_, DO_(args) { // -S - on_with(args[0]); + on_with(args[0].as_string(), args[1]); parent->HANDLER(sort_xacts_).off(); parent->HANDLER(sort_all_).off(); }); OPTION_(report_t, sort_all_, DO_(args) { - parent->HANDLER(sort_).on_with(args[0]); + parent->HANDLER(sort_).on_with(string("--sort-all"), args[1]); parent->HANDLER(sort_xacts_).off(); }); OPTION_(report_t, sort_xacts_, DO_(args) { - parent->HANDLER(sort_).on_with(args[0]); + parent->HANDLER(sort_).on_with(string("--sort-xacts"), args[1]); parent->HANDLER(sort_all_).off(); }); @@ -604,32 +744,30 @@ public: (report_t, total_, // -T expr_t expr; CTOR(report_t, total_) { - set_expr("total"); + set_expr(none, "total"); } - void set_expr(const string& str) { + void set_expr(const optional<string>& whence, const string& str) { expr = str; - on(str); + on(whence, str); } DO_(args) { - set_expr(args[0].to_string()); + set_expr(args[0].to_string(), args[1].to_string()); }); - OPTION_(report_t, total_data, DO() { // -J - parent->HANDLER(format_).on_with(parent->HANDLER(plot_total_format_).value); - }); + OPTION(report_t, total_data); - OPTION_(report_t, truncate_, DO() { -#if 0 - string style(args[0].to_string()); + OPTION_(report_t, truncate_, DO_(args) { + string style(args[1].to_string()); if (style == "leading") - format_t::elision_style = format_t::TRUNCATE_LEADING; + format_t::default_style = format_t::TRUNCATE_LEADING; else if (style == "middle") - format_t::elision_style = format_t::TRUNCATE_MIDDLE; + format_t::default_style = format_t::TRUNCATE_MIDDLE; else if (style == "trailing") - format_t::elision_style = format_t::TRUNCATE_TRAILING; - else if (style == "abbrev") - format_t::elision_style = format_t::ABBREVIATE; -#endif + format_t::default_style = format_t::TRUNCATE_TRAILING; + else + throw_(std::invalid_argument, + _("Unrecognized truncation style: '%1'") << style); + format_t::default_style_changed = true; }); OPTION_(report_t, unbudgeted, DO() { @@ -637,54 +775,72 @@ public: }); OPTION_(report_t, uncleared, DO() { // -U - parent->HANDLER(limit_).on("uncleared|pending"); + parent->HANDLER(limit_).on(string("--uncleared"), "uncleared|pending"); }); OPTION_(report_t, unround, DO() { - parent->HANDLER(amount_).set_expr("unrounded(amount)"); + parent->HANDLER(display_amount_) + .set_expr(string("--unround"), "unrounded(amount_expr)"); + parent->HANDLER(display_total_) + .set_expr(string("--unround"), "unrounded(total_expr)"); }); OPTION_(report_t, weekly, DO() { // -W - parent->HANDLER(period_).on("weekly"); + parent->HANDLER(period_).on(string("--weekly"), "weekly"); }); OPTION_(report_t, wide, DO() { // -w - parent->HANDLER(date_width_).on_with(9L); + parent->HANDLER(date_width_).on_with(string("--wide"), 9L); parent->HANDLER(date_width_).specified = true; - parent->HANDLER(payee_width_).on_with(35L); + parent->HANDLER(payee_width_).on_with(string("--wide"), 35L); parent->HANDLER(payee_width_).specified = true; - parent->HANDLER(account_width_).on_with(39L); + parent->HANDLER(account_width_).on_with(string("--wide"), 39L); parent->HANDLER(account_width_).specified = true; - parent->HANDLER(amount_width_).on_with(22L); + parent->HANDLER(amount_width_).on_with(string("--wide"), 22L); parent->HANDLER(amount_width_).specified = true; - parent->HANDLER(total_width_).on_with(22L); + parent->HANDLER(total_width_).on_with(string("--wide"), 22L); parent->HANDLER(total_width_).specified = true; }); OPTION_(report_t, yearly, DO() { // -Y - parent->HANDLER(period_).on("yearly"); + parent->HANDLER(period_).on(string("--yearly"), "yearly"); }); OPTION__(report_t, date_width_, bool specified; - CTOR(report_t, date_width_) { on_with(9L); specified = false; } - DO_(args) { value = args[0].to_long(); specified = true; }); + CTOR(report_t, date_width_) { + on_with(none, 9L); + specified = false; + } + DO_(args) { value = args[1].to_long(); specified = true; }); OPTION__(report_t, payee_width_, bool specified; - CTOR(report_t, payee_width_) { on_with(20L); specified = false; } - DO_(args) { value = args[0].to_long(); specified = true; }); + CTOR(report_t, payee_width_) { + on_with(none, 20L); + specified = false; + } + DO_(args) { value = args[1].to_long(); specified = true; }); OPTION__(report_t, account_width_, bool specified; - CTOR(report_t, account_width_) { on_with(23L); specified = false; } - DO_(args) { value = args[0].to_long(); specified = true; }); + CTOR(report_t, account_width_) { + on_with(none, 23L); + specified = false; + } + DO_(args) { value = args[1].to_long(); specified = true; }); OPTION__(report_t, amount_width_, bool specified; - CTOR(report_t, amount_width_) { on_with(12L); specified = false; } - DO_(args) { value = args[0].to_long(); specified = true; }); + CTOR(report_t, amount_width_) { + on_with(none, 12L); + specified = false; + } + DO_(args) { value = args[1].to_long(); specified = true; }); OPTION__(report_t, total_width_, bool specified; - CTOR(report_t, total_width_) { on_with(12L); specified = false; } - DO_(args) { value = args[0].to_long(); specified = true; }); + CTOR(report_t, total_width_) { + on_with(none, 12L); + specified = false; + } + DO_(args) { value = args[1].to_long(); specified = true; }); }; } // namespace ledger diff --git a/src/session.cc b/src/session.cc index bbc196df..f7a8655b 100644 --- a/src/session.cc +++ b/src/session.cc @@ -33,6 +33,7 @@ #include "session.h" #include "commodity.h" +#include "pool.h" #include "xact.h" #include "account.h" #include "journal.h" @@ -71,9 +72,9 @@ session_t::session_t() TRACE_CTOR(session_t, ""); if (const char * home_var = std::getenv("HOME")) - HANDLER(price_db_).on((path(home_var) / ".pricedb").string()); + HANDLER(price_db_).on(none, (path(home_var) / ".pricedb").string()); else - HANDLER(price_db_).on(path("./.pricedb").string()); + HANDLER(price_db_).on(none, path("./.pricedb").string()); // Add time commodity conversions, so that timelog's may be parsed // in terms of seconds, but reported as minutes or hours. @@ -81,6 +82,12 @@ session_t::session_t() commodity->add_flags(COMMODITY_BUILTIN | COMMODITY_NOMARKET); else assert(false); + + // Add a "percentile" commodity + if (commodity_t * commodity = commodity_pool->create("%")) + commodity->add_flags(COMMODITY_BUILTIN | COMMODITY_NOMARKET); + else + assert(false); } std::size_t session_t::read_journal(std::istream& in, @@ -213,6 +220,12 @@ void session_t::clean_accounts() option_t<session_t> * session_t::lookup_option(const char * p) { switch (*p) { + case 'Q': + OPT_CH(download); // -Q + break; + case 'Z': + OPT_CH(price_exp_); + break; case 'a': OPT_(account_); // -a break; @@ -226,17 +239,15 @@ option_t<session_t> * session_t::lookup_option(const char * p) OPT(input_date_format_); break; case 'l': - OPT(leeway_); + OPT_ALT(price_exp_, leeway_); break; case 'p': OPT(price_db_); + else OPT(price_exp_); break; case 's': OPT(strict); break; - case 'Q': - OPT_CH(download); // -Q - break; } return NULL; } @@ -245,27 +256,12 @@ expr_t::ptr_op_t session_t::lookup(const string& name) { const char * p = name.c_str(); switch (*p) { - case 'd': - if (is_eq(p, "date")) - return MAKE_FUNCTOR(session_t::fn_today); - break; - - case 'n': - if (is_eq(p, "now")) - return MAKE_FUNCTOR(session_t::fn_now); - break; - case 'o': if (WANT_OPT()) { p += OPT_PREFIX_LEN; if (option_t<session_t> * handler = lookup_option(p)) return MAKE_OPT_HANDLER(session_t, handler); } break; - - case 't': - if (is_eq(p, "today")) - return MAKE_FUNCTOR(session_t::fn_today); - break; } // Check if they are trying to access an option's setting or value. diff --git a/src/session.h b/src/session.h index 1d700d1b..5d6a12b9 100644 --- a/src/session.h +++ b/src/session.h @@ -101,11 +101,15 @@ public: clean_accounts(); } - value_t fn_now(call_scope_t&) { - return CURRENT_TIME(); - } - value_t fn_today(call_scope_t&) { - return CURRENT_DATE(); + void report_options(std::ostream& out) + { + HANDLER(account_).report(out); + HANDLER(download).report(out); + HANDLER(file_).report(out); + HANDLER(input_date_format_).report(out); + HANDLER(price_db_).report(out); + HANDLER(price_exp_).report(out); + HANDLER(strict).report(out); } option_t<session_t> * lookup_option(const char * p); @@ -120,10 +124,10 @@ public: OPTION(session_t, download); // -Q OPTION__ - (session_t, leeway_, - CTOR(session_t, leeway_) { value = 24L * 3600L; } + (session_t, price_exp_, // -Z + CTOR(session_t, price_exp_) { value = 24L * 3600L; } DO_(args) { - value = args[0].to_long() * 60L; + value = args[1].to_long() * 60L; }); OPTION__ @@ -131,15 +135,20 @@ public: std::list<path> data_files; CTOR(session_t, file_) {} DO_(args) { - assert(args.size() == 1); + assert(args.size() == 2); if (parent->flush_on_next_data_file) { data_files.clear(); parent->flush_on_next_data_file = false; } - data_files.push_back(args[0].as_string()); + data_files.push_back(args[1].as_string()); }); - OPTION(session_t, input_date_format_); + OPTION_(session_t, input_date_format_, DO_(args) { + // This changes the global variable inside times.h, which affects the + // basic date parser + input_date_format = args[1].as_string(); + }); + OPTION(session_t, price_db_); OPTION(session_t, strict); }; diff --git a/src/textual.cc b/src/textual.cc index 0c92f7bb..35fa0028 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -37,6 +37,7 @@ #include "account.h" #include "option.h" #include "pstream.h" +#include "pool.h" #define TIMELOG_SUPPORT 1 #if defined(TIMELOG_SUPPORT) @@ -455,67 +456,18 @@ void instance_t::price_conversion_directive(char * line) } } -namespace { - void parse_symbol(char *& p, string& symbol) - { - if (*p == '"') { - char * q = std::strchr(p + 1, '"'); - if (! q) - throw parse_error(_("Quoted commodity symbol lacks closing quote")); - symbol = string(p + 1, 0, q - p - 1); - p = q + 2; - } else { - char * q = next_element(p); - symbol = p; - if (q) - p = q; - else - p += symbol.length(); - } - if (symbol.empty()) - throw parse_error(_("Failed to parse commodity")); - } -} - void instance_t::price_xact_directive(char * line) { - char * date_field_ptr = skip_ws(line + 1); - char * time_field_ptr = next_element(date_field_ptr); - if (! time_field_ptr) return; - string date_field = date_field_ptr; - - char * symbol_and_price; - datetime_t datetime; - - if (std::isdigit(time_field_ptr[0])) { - symbol_and_price = next_element(time_field_ptr); - if (! symbol_and_price) return; - datetime = parse_datetime(date_field + " " + time_field_ptr, - current_year); - } else { - symbol_and_price = time_field_ptr; - datetime = parse_datetime(date_field, current_year); - } - - string symbol; - parse_symbol(symbol_and_price, symbol); - amount_t price(symbol_and_price); - VERIFY(price.valid()); - - if (commodity_t * commodity = - amount_t::current_pool->find_or_create(symbol)) { - commodity->add_price(datetime, price, true); - commodity->add_flags(COMMODITY_KNOWN); - } else { - assert(false); - } + optional<price_point_t> point = + amount_t::current_pool->parse_price_directive(skip_ws(line + 1)); + assert(point); } void instance_t::nomarket_directive(char * line) { char * p = skip_ws(line + 1); string symbol; - parse_symbol(p, symbol); + commodity_t::parse_symbol(p, symbol); if (commodity_t * commodity = amount_t::current_pool->find_or_create(symbol)) @@ -535,7 +487,7 @@ void instance_t::option_directive(char * line) if (p) *p++ = '\0'; } - process_option(line + 2, session_scope, p, line); + process_option(pathname.string(), line + 2, session_scope, p, line); } void instance_t::automated_xact_directive(char * line) @@ -863,6 +815,9 @@ post_t * instance_t::parse_post(char * line, char * next = next_element(p, true); char * e = p + std::strlen(p); + while (e > p && std::isspace(*(e - 1))) + e--; + if ((*p == '[' && *(e - 1) == ']') || (*p == '(' && *(e - 1) == ')')) { post->add_flags(POST_VIRTUAL); DEBUG("textual.parse", "line " << linenum << ": " @@ -966,8 +921,14 @@ post_t * instance_t::parse_post(char * line, post->cost->in_place_unround(); - if (per_unit) + if (per_unit) { + // For the sole case where the cost might be uncommoditized, + // guarantee that the commodity of the cost after multiplication + // is the same as it was before. + commodity_t& cost_commodity(post->cost->commodity()); *post->cost *= post->amount; + post->cost->set_commodity(cost_commodity); + } DEBUG("textual.parse", "line " << linenum << ": " << "Total cost is " << *post->cost); @@ -1014,7 +975,8 @@ post_t * instance_t::parse_post(char * line, << "POST assign: parsed amt = " << *post->assigned_amount); amount_t& amt(*post->assigned_amount); - value_t account_total(post->account->self_total(false)); + value_t account_total(post->account->self_total(false) + .strip_annotations(keep_details_t())); DEBUG("post.assign", "line " << linenum << ": " "account balance = " << account_total); diff --git a/src/times.cc b/src/times.cc index 360f786a..deb51058 100644 --- a/src/times.cc +++ b/src/times.cc @@ -649,15 +649,19 @@ void date_interval_t::parse(std::istream& in) if (! end) end = *start + gregorian::days(1); } else { + bool overwrite_end = false; + if (year) { start = date_t(*year, 1, 1); - if (! end) + if (! end) { end = *start + gregorian::years(1); + overwrite_end = true; + } } if (mon) { start = date_t(start->year(), *mon, 1); - if (! end) + if (! end || overwrite_end) end = *start + gregorian::months(1); } } diff --git a/src/token.cc b/src/token.cc index d173c7ec..fde6a0e3 100644 --- a/src/token.cc +++ b/src/token.cc @@ -278,11 +278,6 @@ void expr_t::token_t::next(std::istream& in, const uint_least8_t pflags) kind = STAR; break; - case '%': - in.get(c); - kind = PERCENT; - break; - case '?': in.get(c); kind = QUERY; diff --git a/src/token.h b/src/token.h index 85374f6e..a9b75dd4 100644 --- a/src/token.h +++ b/src/token.h @@ -84,9 +84,9 @@ struct expr_t::token_t : public noncopyable KW_DIV, // div EXCLAM, // !, not - PERCENT, // % KW_AND, // &, &&, and KW_OR, // |, ||, or + KW_MOD, // % KW_IF, // if KW_ELSE, // else diff --git a/src/unistring.h b/src/unistring.h index b3086c39..5467a151 100644 --- a/src/unistring.h +++ b/src/unistring.h @@ -99,10 +99,14 @@ public: inline void justify(std::ostream& out, const std::string& str, int width, - bool right = false) + bool right = false, + bool redden = false) { - if (! right) + if (! right) { + if (redden) out << "\e[31m"; out << str; + if (redden) out << "\e[0m"; + } unistring temp(str); @@ -110,8 +114,11 @@ inline void justify(std::ostream& out, while (spacing-- > 0) out << ' '; - if (right) + if (right) { + if (redden) out << "\e[31m"; out << str; + if (redden) out << "\e[0m"; + } } } // namespace ledger diff --git a/src/value.cc b/src/value.cc index 53e2bdeb..702b889e 100644 --- a/src/value.cc +++ b/src/value.cc @@ -33,6 +33,8 @@ #include "value.h" #include "commodity.h" +#include "annotate.h" +#include "pool.h" #include "unistring.h" namespace ledger { @@ -573,6 +575,7 @@ value_t& value_t::operator*=(const value_t& val) return *this; case AMOUNT: if (as_amount().commodity() == val.as_amount().commodity() || + ! as_amount().has_commodity() || ! val.as_amount().has_commodity()) { as_amount_lval() *= val.as_amount(); return *this; @@ -1227,6 +1230,39 @@ value_t value_t::value(const bool primary_only, return NULL_VALUE; } +value_t value_t::price() const +{ + switch (type()) { + case AMOUNT: + return as_amount().price(); + case BALANCE: + return as_balance().price(); + default: + return *this; + } +} + +value_t value_t::exchange_commodities(const std::string& commodities, + const bool add_prices, + const optional<datetime_t>& moment) +{ + scoped_array<char> buf(new char[commodities.length() + 1]); + + std::strcpy(buf.get(), commodities.c_str()); + + for (char * p = std::strtok(buf.get(), ","); + p; + p = std::strtok(NULL, ",")) { + if (commodity_t * commodity = + amount_t::current_pool->parse_price_expression(p, add_prices, moment)) { + value_t result = value(false, moment, *commodity); + if (! result.is_null()) + return result; + } + } + return *this; +} + void value_t::in_place_reduce() { switch (type()) { @@ -1336,10 +1372,10 @@ void value_t::in_place_unround() case INTEGER: return; case AMOUNT: - as_amount_lval().unrounded(); + as_amount_lval().in_place_unround(); return; case BALANCE: - as_balance_lval().unrounded(); + as_balance_lval().in_place_unround(); return; case SEQUENCE: { value_t temp; @@ -1423,10 +1459,12 @@ void value_t::print(std::ostream& out, const int first_width, const int latter_width, const bool right_justify, + const bool colorize, const optional<string>& date_format) const { if (first_width > 0 && - ! is_amount() && ! is_balance() && ! is_string()) { + (! is_amount() || as_amount().is_zero()) && + ! is_balance() && ! is_string()) { out.width(first_width); if (right_justify) @@ -1459,17 +1497,20 @@ void value_t::print(std::ostream& out, break; case INTEGER: - out << std::right << as_long(); + if (colorize && as_long() < 0) + justify(out, to_string(), first_width, right_justify, true); + else + out << as_long(); break; case AMOUNT: { if (as_amount().is_zero()) { - out.width(first_width); - out << (right_justify ? std::right : std::left) << 0; + out << 0; } else { std::ostringstream buf; buf << as_amount(); - justify(out, buf.str(), first_width, right_justify); + justify(out, buf.str(), first_width, right_justify, + colorize && as_amount().sign() < 0); } break; } @@ -1492,14 +1533,15 @@ void value_t::print(std::ostream& out, out << ", "; value.print(out, first_width, latter_width, right_justify, - date_format); + colorize, date_format); } out << ')'; break; } case BALANCE: - as_balance().print(out, first_width, latter_width, right_justify); + as_balance().print(out, first_width, latter_width, right_justify, + colorize); break; case POINTER: @@ -1549,7 +1591,21 @@ void value_t::dump(std::ostream& out, const bool relaxed) const break; case STRING: - out << '"' << as_string() << '"'; + out << '"'; + foreach (const char& ch, as_string()) { + switch (ch) { + case '"': + out << "\\\""; + break; + case '\\': + out << "\\\\"; + break; + default: + out << ch; + break; + } + } + out << '"'; break; case MASK: diff --git a/src/value.h b/src/value.h index 1e972558..62943e62 100644 --- a/src/value.h +++ b/src/value.h @@ -449,6 +449,12 @@ public: const optional<datetime_t>& moment = none, const optional<commodity_t&>& in_terms_of = none) const; + value_t price() const; + + value_t exchange_commodities(const std::string& commodities, + const bool add_prices = false, + const optional<datetime_t>& moment = none); + /** * Truth tests. */ @@ -915,6 +921,7 @@ public: const int first_width = -1, const int latter_width = -1, const bool right_justify = false, + const bool colorize = false, const optional<string>& date_format = none) const; void dump(std::ostream& out, const bool relaxed = true) const; diff --git a/src/xact.cc b/src/xact.cc index 9e5322fa..bd8a5955 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -35,6 +35,7 @@ #include "post.h" #include "account.h" #include "journal.h" +#include "pool.h" namespace ledger { @@ -269,9 +270,9 @@ bool xact_base_t::finalize() throw_(balance_error, _("A posting's cost must be of a different commodity than its amount")); - commodity_t::cost_breakdown_t breakdown = - commodity_t::exchange(post->amount, *post->cost, false, - datetime_t(date(), time_duration(0, 0, 0, 0))); + cost_breakdown_t breakdown = + amount_t::current_pool->exchange(post->amount, *post->cost, false, + datetime_t(date(), time_duration(0, 0, 0, 0))); if (post->amount.is_annotated() && breakdown.basis_cost.commodity() == @@ -387,10 +388,16 @@ expr_t::ptr_op_t xact_t::lookup(const string& name) bool xact_t::valid() const { - if (! _date || ! journal) { - DEBUG("ledger.validate", "xact_t: ! _date || ! journal"); + if (! _date) { + DEBUG("ledger.validate", "xact_t: ! _date"); return false; } +#if 0 + if (! journal) { + DEBUG("ledger.validate", "xact_t: ! journal"); + return false; + } +#endif foreach (post_t * post, posts) if (post->xact != this || ! post->valid()) { @@ -206,7 +206,13 @@ class period_xact_t : public xact_base_t } virtual bool valid() const { - return period.is_valid(); +#if 0 + if (! period.is_valid()) { + DEBUG("ledger.validate", "period_xact_t: ! period.is_valid()"); + return false; + } +#endif + return true; } }; diff --git a/test/baseline/feat-balance-assignments.test b/test/baseline/feat-balance-assignments.test new file mode 100644 index 00000000..74fe9ddb --- /dev/null +++ b/test/baseline/feat-balance-assignments.test @@ -0,0 +1,37 @@ +bal +<<< +2009/01/01 Entry + Assets:Cash $10,000.00 + Equity:Opening Balances + +2009/02/01 Entry + Expenses:Cash $100.00 + Assets:Cash + +2009/02/02 Entry + Expenses:Cash $100.00 + Assets:Cash + +2009/02/03 Entry + Expenses:Cash $100.00 + Assets:Cash $-100.00 = $9,700.00 + +2009/02/04 Entry + Expenses:Cash $100.00 + Assets:Cash $-100.00 = $9,600.00 + +2009/02/05 Entry + Expenses:Cash $100.00 + Assets:Cash + +2009/02/05 Entry + Expenses:Cash + Assets:Cash = ($4,000.00 + $100.00) +>>>1 + $4,100.00 Assets:Cash + $-10,000.00 Equity:Opening Balances + $5,900.00 Expenses:Cash +-------------------- + 0 +>>>2 +=== 0 diff --git a/test/baseline/feature-fixated-prices.test b/test/baseline/feat-fixated-prices.test index 11330dea..11330dea 100644 --- a/test/baseline/feature-fixated-prices.test +++ b/test/baseline/feat-fixated-prices.test diff --git a/test/baseline/opt-anon.test b/test/baseline/opt-anon.test index fb8acd64..f9244451 100644 --- a/test/baseline/opt-anon.test +++ b/test/baseline/opt-anon.test @@ -4,8 +4,8 @@ reg --anon Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 Income:Dividends:Vanguard:VMMXX $-0.35 >>>1 -07-Feb-02 6a93dcb30aa7722e967.. ..cfb2807d6a978b431fc7 0.350 VMMXX 0.350 VMMXX - ..4fbac0065bfcc300a24e $-0.35 $-0.35 +07-Feb-02 6a93dcb3 20:5d:27:988a9c3a 0.350 VMMXX 0.350 VMMXX + 1c:b6:27:988a9c3a $-0.35 $-0.35 0.350 VMMXX >>>2 === 0 diff --git a/test/baseline/opt-color.test b/test/baseline/opt-color.test new file mode 100644 index 00000000..169c46ae --- /dev/null +++ b/test/baseline/opt-color.test @@ -0,0 +1,13 @@ +bal --color +<<< +2007/02/02 RD VMMXX + Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 + Income:Dividends:Vanguard:VMMXX $-0.35 +>>>1 + 0.350 VMMXX [34mAssets:Investments:Vanguard:VMMXX[0m + [31m$-0.35[0m [34mIncome:Dividends:Vanguard:VMMXX[0m +-------------------- + [31m$-0.35[0m + 0.350 VMMXX +>>>2 +=== 0 diff --git a/test/baseline/opt-comm-as-account.test b/test/baseline/opt-comm-as-account.test new file mode 100644 index 00000000..c92a47fd --- /dev/null +++ b/test/baseline/opt-comm-as-account.test @@ -0,0 +1,11 @@ +reg --comm-as-account +<<< +2007/02/02 RD VMMXX + Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 + Income:Dividends:Vanguard:VMMXX $-0.35 +>>>1 +07-Feb-02 RD VMMXX VMMXX 0.350 VMMXX 0.350 VMMXX +07-Feb-02 RD VMMXX $ $-0.35 $-0.35 + 0.350 VMMXX +>>>2 +=== 0 diff --git a/test/baseline/opt-comm-as-payee.test b/test/baseline/opt-comm-as-payee.test new file mode 100644 index 00000000..94b37b85 --- /dev/null +++ b/test/baseline/opt-comm-as-payee.test @@ -0,0 +1,11 @@ +reg --comm-as-payee +<<< +2007/02/02 RD VMMXX + Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 + Income:Dividends:Vanguard:VMMXX $-0.35 +>>>1 +07-Feb-02 VMMXX As:In:Vanguard:VMMXX 0.350 VMMXX 0.350 VMMXX +07-Feb-02 $ In:Di:Vanguard:VMMXX $-0.35 $-0.35 + 0.350 VMMXX +>>>2 +=== 0 diff --git a/test/baseline/opt-input-date-format.test b/test/baseline/opt-input-date-format.test new file mode 100644 index 00000000..0ab5e5c9 --- /dev/null +++ b/test/baseline/opt-input-date-format.test @@ -0,0 +1,11 @@ +reg --input-date-format='%m%%%d%%%Y' +<<< +02%02%2007 RD VMMXX + Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 + Income:Dividends:Vanguard:VMMXX $-0.35 +>>>1 +07-Feb-02 RD VMMXX As:In:Vanguard:VMMXX 0.350 VMMXX 0.350 VMMXX + In:Di:Vanguard:VMMXX $-0.35 $-0.35 + 0.350 VMMXX +>>>2 +=== 0 diff --git a/test/baseline/opt-limit.test b/test/baseline/opt-limit.test new file mode 100644 index 00000000..dd302d1b --- /dev/null +++ b/test/baseline/opt-limit.test @@ -0,0 +1,212 @@ +reg --limit='account =~ /Books/ and amount < 50' +<<< +2008/01/01 January + Expenses:Books $10.00 + Assets:Cash + +2008/01/31 End of January + Expenses:Books $10.00 + Assets:Cash + +2008/02/01 February + Expenses:Books $20.00 + Assets:Cash + +2008/02/28 End of February + Expenses:Books $20.00 + Assets:Cash + +2008/03/01 March + Expenses:Books $30.00 + Assets:Cash + +2008/03/31 End of March + Expenses:Books $30.00 + Assets:Cash + +2008/04/01 April + Expenses:Books $40.00 + Assets:Cash + +2008/04/30 End of April + Expenses:Books $40.00 + Assets:Cash + +2008/05/01 May + Expenses:Books $50.00 + Assets:Cash + +2008/05/31 End of May + Expenses:Books $50.00 + Assets:Cash + +2008/06/01 June + Expenses:Books $60.00 + Assets:Cash + +2008/06/30 End of June + Expenses:Books $60.00 + Assets:Cash + +2008/07/01 July + Expenses:Books $70.00 + Assets:Cash + +2008/07/31 End of July + Expenses:Books $70.00 + Assets:Cash + +2008/08/01 August + Expenses:Books $80.00 + Assets:Cash + +2008/08/31 End of August + Expenses:Books $80.00 + Assets:Cash + +2008/09/01 September + Expenses:Books $90.00 + Assets:Cash + +2008/09/30 End of September + Expenses:Books $90.00 + Assets:Cash + +2008/10/01 October + Expenses:Books $100.00 + Assets:Cash + +2008/10/31 End of October + Expenses:Books $100.00 + Assets:Cash + +2008/11/01 November + Expenses:Books $110.00 + Assets:Cash + +2008/11/30 End of November + Expenses:Books $110.00 + Assets:Cash + +2008/12/01 December + Expenses:Books $120.00 + Assets:Cash + +2008/12/31 End of December + Expenses:Books $120.00 + Assets:Cash + +2009/01/01 January + Expenses:Books $10.00 + Assets:Cash + +2009/01/31 End of January + Expenses:Books $10.00 + Assets:Cash + +2009/02/01 February + Expenses:Books $20.00 + Assets:Cash + +2009/02/28 End of February + Expenses:Books $20.00 + Assets:Cash + +2009/03/01 March + Expenses:Books $30.00 + Assets:Cash + +2009/03/31 End of March + Expenses:Books $30.00 + Assets:Cash + +2009/04/01 April + Expenses:Books $40.00 + Assets:Cash + +2009/04/30 End of April + Expenses:Books $40.00 + Assets:Cash + +2009/05/01 May + Expenses:Books $50.00 + Assets:Cash + +2009/05/31 End of May + Expenses:Books $50.00 + Assets:Cash + +2009/06/01 June + Expenses:Books $60.00 + Assets:Cash + +2009/06/30 End of June + Expenses:Books $60.00 + Assets:Cash + +2009/07/01 July + Expenses:Books $70.00 + Assets:Cash + +2009/07/31 End of July + Expenses:Books $70.00 + Assets:Cash + +2009/08/01 August + Expenses:Books $80.00 + Assets:Cash + +2009/08/31 End of August + Expenses:Books $80.00 + Assets:Cash + +2009/09/01 September + Expenses:Books $90.00 + Assets:Cash + +2009/09/30 End of September + Expenses:Books $90.00 + Assets:Cash + +2009/10/01 October + Expenses:Books $100.00 + Assets:Cash + +2009/10/31 End of October + Expenses:Books $100.00 + Assets:Cash + +2009/11/01 November + Expenses:Books $110.00 + Assets:Cash + +2009/11/30 End of November + Expenses:Books $110.00 + Assets:Cash + +2009/12/01 December + Expenses:Books $120.00 + Assets:Cash + +2009/12/31 End of December + Expenses:Books $120.00 + Assets:Cash +>>>1 +08-Jan-01 January Expenses:Books $10.00 $10.00 +08-Jan-31 End of January Expenses:Books $10.00 $20.00 +08-Feb-01 February Expenses:Books $20.00 $40.00 +08-Feb-28 End of February Expenses:Books $20.00 $60.00 +08-Mar-01 March Expenses:Books $30.00 $90.00 +08-Mar-31 End of March Expenses:Books $30.00 $120.00 +08-Apr-01 April Expenses:Books $40.00 $160.00 +08-Apr-30 End of April Expenses:Books $40.00 $200.00 +09-Jan-01 January Expenses:Books $10.00 $210.00 +09-Jan-31 End of January Expenses:Books $10.00 $220.00 +09-Feb-01 February Expenses:Books $20.00 $240.00 +09-Feb-28 End of February Expenses:Books $20.00 $260.00 +09-Mar-01 March Expenses:Books $30.00 $290.00 +09-Mar-31 End of March Expenses:Books $30.00 $320.00 +09-Apr-01 April Expenses:Books $40.00 $360.00 +09-Apr-30 End of April Expenses:Books $40.00 $400.00 +>>>2 +=== 0 diff --git a/test/baseline/opt-lot-tags.test b/test/baseline/opt-lot-tags.test new file mode 100644 index 00000000..d77aea33 --- /dev/null +++ b/test/baseline/opt-lot-tags.test @@ -0,0 +1,12 @@ +bal --lot-tags tajer +<<< +D 1.00c + +2006/03/14 Opening Balances + Assets:Tajer 1339829c (TAG) + Assets:Gruulmorg 248720c + Equity:Gold +>>>1 + 1339829.00c (TAG) Assets:Tajer +>>>2 +=== 0 diff --git a/test/baseline/opt-lots-actual.test b/test/baseline/opt-lots-actual.test new file mode 100644 index 00000000..39a27363 --- /dev/null +++ b/test/baseline/opt-lots-actual.test @@ -0,0 +1,27 @@ +reg --format '%(justify(scrub(total_expr), 40, 40, true))\n' --lots +<<< +D 1.0000s + +2006/03/14 Opening Balances + Assets:Tajer 1339829c @ 1.86590975416s + Assets:Gruulmorg 248720c {10.051463493s} + Equity:Gold -5000000s +>>>1 + 1339829c {1.8659s} [2006/03/14] + 1339829c {1.8659s} [2006/03/14] + 248720c {10.0515s} + 1339829c {1.8659s} [2006/03/14] + 248720c {10.0515s} + -1388.9h +>>>2 +=== 0 +reg --format '%(justify(scrub(total_expr), 40, 40, true))\n' --lots-actual +>>>1 + 1339829c + 1339829c + 248720c {10.0515s} + 1339829c + 248720c {10.0515s} + -1388.9h +>>>2 +=== 0 diff --git a/test/baseline/opt-no-total.test b/test/baseline/opt-no-total.test new file mode 100644 index 00000000..d1a11485 --- /dev/null +++ b/test/baseline/opt-no-total.test @@ -0,0 +1,10 @@ +bal --no-total +<<< +2007/02/02 RD VMMXX + Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 + Income:Dividends:Vanguard:VMMXX $-0.35 +>>>1 + 0.350 VMMXX Assets:Investments:Vanguard:VMMXX + $-0.35 Income:Dividends:Vanguard:VMMXX +>>>2 +=== 0 diff --git a/test/baseline/opt-only.test b/test/baseline/opt-only.test new file mode 100644 index 00000000..9728edae --- /dev/null +++ b/test/baseline/opt-only.test @@ -0,0 +1,236 @@ +reg books --monthly --limit='amount > $100' +<<< +2008/01/01 January + Expenses:Books $10.00 + Assets:Cash + +2008/01/31 End of January + Expenses:Books $10.00 + Assets:Cash + +2008/02/01 February + Expenses:Books $20.00 + Assets:Cash + +2008/02/28 End of February + Expenses:Books $20.00 + Assets:Cash + +2008/03/01 March + Expenses:Books $30.00 + Assets:Cash + +2008/03/31 End of March + Expenses:Books $30.00 + Assets:Cash + +2008/04/01 April + Expenses:Books $40.00 + Assets:Cash + +2008/04/30 End of April + Expenses:Books $40.00 + Assets:Cash + +2008/05/01 May + Expenses:Books $50.00 + Assets:Cash + +2008/05/31 End of May + Expenses:Books $50.00 + Assets:Cash + +2008/06/01 June + Expenses:Books $60.00 + Assets:Cash + +2008/06/30 End of June + Expenses:Books $60.00 + Assets:Cash + +2008/07/01 July + Expenses:Books $70.00 + Assets:Cash + +2008/07/31 End of July + Expenses:Books $70.00 + Assets:Cash + +2008/08/01 August + Expenses:Books $80.00 + Assets:Cash + +2008/08/31 End of August + Expenses:Books $80.00 + Assets:Cash + +2008/09/01 September + Expenses:Books $90.00 + Assets:Cash + +2008/09/30 End of September + Expenses:Books $90.00 + Assets:Cash + +2008/10/01 October + Expenses:Books $100.00 + Assets:Cash + +2008/10/31 End of October + Expenses:Books $100.00 + Assets:Cash + +2008/11/01 November + Expenses:Books $110.00 + Assets:Cash + +2008/11/30 End of November + Expenses:Books $110.00 + Assets:Cash + +2008/12/01 December + Expenses:Books $120.00 + Assets:Cash + +2008/12/31 End of December + Expenses:Books $120.00 + Assets:Cash + +2009/01/01 January + Expenses:Books $10.00 + Assets:Cash + +2009/01/31 End of January + Expenses:Books $10.00 + Assets:Cash + +2009/02/01 February + Expenses:Books $20.00 + Assets:Cash + +2009/02/28 End of February + Expenses:Books $20.00 + Assets:Cash + +2009/03/01 March + Expenses:Books $30.00 + Assets:Cash + +2009/03/31 End of March + Expenses:Books $30.00 + Assets:Cash + +2009/04/01 April + Expenses:Books $40.00 + Assets:Cash + +2009/04/30 End of April + Expenses:Books $40.00 + Assets:Cash + +2009/05/01 May + Expenses:Books $50.00 + Assets:Cash + +2009/05/31 End of May + Expenses:Books $50.00 + Assets:Cash + +2009/06/01 June + Expenses:Books $60.00 + Assets:Cash + +2009/06/30 End of June + Expenses:Books $60.00 + Assets:Cash + +2009/07/01 July + Expenses:Books $70.00 + Assets:Cash + +2009/07/31 End of July + Expenses:Books $70.00 + Assets:Cash + +2009/08/01 August + Expenses:Books $80.00 + Assets:Cash + +2009/08/31 End of August + Expenses:Books $80.00 + Assets:Cash + +2009/09/01 September + Expenses:Books $90.00 + Assets:Cash + +2009/09/30 End of September + Expenses:Books $90.00 + Assets:Cash + +2009/10/01 October + Expenses:Books $100.00 + Assets:Cash + +2009/10/31 End of October + Expenses:Books $100.00 + Assets:Cash + +2009/11/01 November + Expenses:Books $110.00 + Assets:Cash + +2009/11/30 End of November + Expenses:Books $110.00 + Assets:Cash + +2009/12/01 December + Expenses:Books $120.00 + Assets:Cash + +2009/12/31 End of December + Expenses:Books $120.00 + Assets:Cash +>>>1 +08-Nov-01 - 08-Nov-30 Expenses:Books $220.00 $220.00 +08-Dec-01 - 08-Dec-31 Expenses:Books $240.00 $460.00 +09-Nov-01 - 09-Nov-30 Expenses:Books $220.00 $680.00 +09-Dec-01 - 09-Dec-31 Expenses:Books $240.00 $920.00 +>>>2 +=== 0 +reg books --monthly --only='amount > $100' +>>>1 +08-Jun-01 - 08-Jun-30 Expenses:Books $120.00 $120.00 +08-Jul-01 - 08-Jul-31 Expenses:Books $140.00 $260.00 +08-Aug-01 - 08-Aug-31 Expenses:Books $160.00 $420.00 +08-Sep-01 - 08-Sep-30 Expenses:Books $180.00 $600.00 +08-Oct-01 - 08-Oct-31 Expenses:Books $200.00 $800.00 +08-Nov-01 - 08-Nov-30 Expenses:Books $220.00 $1020.00 +08-Dec-01 - 08-Dec-31 Expenses:Books $240.00 $1260.00 +09-Jun-01 - 09-Jun-30 Expenses:Books $120.00 $1380.00 +09-Jul-01 - 09-Jul-31 Expenses:Books $140.00 $1520.00 +09-Aug-01 - 09-Aug-31 Expenses:Books $160.00 $1680.00 +09-Sep-01 - 09-Sep-30 Expenses:Books $180.00 $1860.00 +09-Oct-01 - 09-Oct-31 Expenses:Books $200.00 $2060.00 +09-Nov-01 - 09-Nov-30 Expenses:Books $220.00 $2280.00 +09-Dec-01 - 09-Dec-31 Expenses:Books $240.00 $2520.00 +>>>2 +=== 0 +reg books --monthly --display='amount > $100' +>>>1 +08-Jun-01 - 08-Jun-30 Expenses:Books $120.00 $420.00 +08-Jul-01 - 08-Jul-31 Expenses:Books $140.00 $560.00 +08-Aug-01 - 08-Aug-31 Expenses:Books $160.00 $720.00 +08-Sep-01 - 08-Sep-30 Expenses:Books $180.00 $900.00 +08-Oct-01 - 08-Oct-31 Expenses:Books $200.00 $1100.00 +08-Nov-01 - 08-Nov-30 Expenses:Books $220.00 $1320.00 +08-Dec-01 - 08-Dec-31 Expenses:Books $240.00 $1560.00 +09-Jun-01 - 09-Jun-30 Expenses:Books $120.00 $1980.00 +09-Jul-01 - 09-Jul-31 Expenses:Books $140.00 $2120.00 +09-Aug-01 - 09-Aug-31 Expenses:Books $160.00 $2280.00 +09-Sep-01 - 09-Sep-30 Expenses:Books $180.00 $2460.00 +09-Oct-01 - 09-Oct-31 Expenses:Books $200.00 $2660.00 +09-Nov-01 - 09-Nov-30 Expenses:Books $220.00 $2880.00 +09-Dec-01 - 09-Dec-31 Expenses:Books $240.00 $3120.00 +>>>2 +=== 0 diff --git a/test/baseline/opt-percent.test b/test/baseline/opt-percent.test new file mode 100644 index 00000000..1adb0a8a --- /dev/null +++ b/test/baseline/opt-percent.test @@ -0,0 +1,94 @@ +bal --percent +<<< +2008/01/11 LIAT + Expenses:Travel:Airfare $40.00 + Liabilities:MasterCard + +2008/01/14 cheaptickets.com + Expenses:Travel:Airfare $182.19 + Liabilities:MasterCard + +2008/02/05 CTX + Expenses:Travel:Auto $240.38 + Liabilities:MasterCard + +2008/02/05 UNITED + Expenses:Travel:Airfare $238.80 + Liabilities:MasterCard + +2008/02/05 UNITED + Expenses:Travel:Airfare $238.80 + Liabilities:MasterCard + +2008/02/22 BUDGET RENT-A-CAR + Expenses:Travel:Auto $40.59 + Liabilities:MasterCard + +2008/03/16 IBERIA + Expenses:Travel:Airfare $1,231.60 + Liabilities:MasterCard + +2008/03/16 IBERIA + Expenses:Travel:Airfare $1,231.60 + Liabilities:MasterCard + +2008/04/03 AMERICAN + Expenses:Travel:Airfare $155.86 + Liabilities:MasterCard + +2008/04/03 AMERICAN + Expenses:Travel:Airfare $155.86 + Liabilities:MasterCard + +2008/04/30 UNITED + Expenses:Travel:Airfare $437.21 + Liabilities:MasterCard + +2008/04/30 UNITED + Expenses:Travel:Airfare $437.21 + Liabilities:MasterCard + +2008/08/08 BCIS I-131 FILING FEE- + Expenses:Travel:Passport $170.00 + Liabilities:MasterCard + +2008/09/06 AMERICAN + Expenses:Travel:Airfare $912.60 + Liabilities:MasterCard + +2008/09/06 AMERICAN + Expenses:Travel:Airfare $912.60 + Liabilities:MasterCard + +2008/09/22 AGNT FEE + Expenses:Travel:Airfare $70.00 + Liabilities:MasterCard + +2008/09/22 DELTA + Expenses:Travel:Airfare $806.20 + Liabilities:MasterCard + +2008/09/22 DELTA + Expenses:Travel:Airfare $806.20 + Liabilities:MasterCard + +2008/09/22 LIAT 1974 LIMITED + Expenses:Travel:Airfare $418.34 + Liabilities:MasterCard + +2008/12/26 U.S. Department of State + Expenses:Travel:Passport $127.00 + Assets:Checking + +2008/12/26 U.S. Department of State + Expenses:Travel:Passport $127.00 + Assets:Checking +>>>1 + 100.00% Assets:Checking + 100.00% Expenses:Travel + 92.15% Airfare + 3.13% Auto + 4.72% Passport + 100.00% Liabilities:MasterCard +>>>2 +=== 0 diff --git a/test/baseline/opt-period.test b/test/baseline/opt-period.test new file mode 100644 index 00000000..09e7e355 --- /dev/null +++ b/test/baseline/opt-period.test @@ -0,0 +1,290 @@ +reg -p "january 2008" +<<< +2008/01/01 January + Expenses:Books $10.00 + Liabilities:Cards $10.00 + Assets:Cash + +2008/01/31 End of January + Expenses:Books $10.00 + Liabilities:Cards $10.00 + Assets:Cash + +2008/02/01 February + Expenses:Books $20.00 + Liabilities:Cards $20.00 + Assets:Cash + +2008/02/28 End of February + Expenses:Books $20.00 + Liabilities:Cards $20.00 + Assets:Cash + +2008/03/01 March + Expenses:Books $30.00 + Liabilities:Cards $30.00 + Assets:Cash + +2008/03/31 End of March + Expenses:Books $30.00 + Liabilities:Cards $30.00 + Assets:Cash + +2008/04/01 April + Expenses:Books $40.00 + Liabilities:Cards $40.00 + Assets:Cash + +2008/04/30 End of April + Expenses:Books $40.00 + Liabilities:Cards $40.00 + Assets:Cash + +2008/05/01 May + Expenses:Books $50.00 + Liabilities:Cards $50.00 + Assets:Cash + +2008/05/31 End of May + Expenses:Books $50.00 + Liabilities:Cards $50.00 + Assets:Cash + +2008/06/01 June + Expenses:Books $60.00 + Liabilities:Cards $60.00 + Assets:Cash + +2008/06/30 End of June + Expenses:Books $60.00 + Liabilities:Cards $60.00 + Assets:Cash + +2008/07/01 July + Expenses:Books $70.00 + Liabilities:Cards $70.00 + Assets:Cash + +2008/07/31 End of July + Expenses:Books $70.00 + Liabilities:Cards $70.00 + Assets:Cash + +2008/08/01 August + Expenses:Books $80.00 + Liabilities:Cards $80.00 + Assets:Cash + +2008/08/31 End of August + Expenses:Books $80.00 + Liabilities:Cards $80.00 + Assets:Cash + +2008/09/01 September + Expenses:Books $90.00 + Liabilities:Cards $90.00 + Assets:Cash + +2008/09/30 End of September + Expenses:Books $90.00 + Liabilities:Cards $90.00 + Assets:Cash + +2008/10/01 October + Expenses:Books $100.00 + Liabilities:Cards $100.00 + Assets:Cash + +2008/10/31 End of October + Expenses:Books $100.00 + Liabilities:Cards $100.00 + Assets:Cash + +2008/11/01 November + Expenses:Books $110.00 + Liabilities:Cards $110.00 + Assets:Cash + +2008/11/30 End of November + Expenses:Books $110.00 + Liabilities:Cards $110.00 + Assets:Cash + +2008/12/01 December + Expenses:Books $120.00 + Liabilities:Cards $120.00 + Assets:Cash + +2008/12/31 End of December + Expenses:Books $120.00 + Liabilities:Cards $120.00 + Assets:Cash + +2009/01/01 January + Expenses:Books $10.00 + Liabilities:Cards $10.00 + Assets:Cash + +2009/01/31 End of January + Expenses:Books $10.00 + Liabilities:Cards $10.00 + Assets:Cash + +2009/02/01 February + Expenses:Books $20.00 + Liabilities:Cards $20.00 + Assets:Cash + +2009/02/28 End of February + Expenses:Books $20.00 + Liabilities:Cards $20.00 + Assets:Cash + +2009/03/01 March + Expenses:Books $30.00 + Liabilities:Cards $30.00 + Assets:Cash + +2009/03/31 End of March + Expenses:Books $30.00 + Liabilities:Cards $30.00 + Assets:Cash + +2009/04/01 April + Expenses:Books $40.00 + Liabilities:Cards $40.00 + Assets:Cash + +2009/04/30 End of April + Expenses:Books $40.00 + Liabilities:Cards $40.00 + Assets:Cash + +2009/05/01 May + Expenses:Books $50.00 + Liabilities:Cards $50.00 + Assets:Cash + +2009/05/31 End of May + Expenses:Books $50.00 + Liabilities:Cards $50.00 + Assets:Cash + +2009/06/01 June + Expenses:Books $60.00 + Liabilities:Cards $60.00 + Assets:Cash + +2009/06/30 End of June + Expenses:Books $60.00 + Liabilities:Cards $60.00 + Assets:Cash + +2009/07/01 July + Expenses:Books $70.00 + Liabilities:Cards $70.00 + Assets:Cash + +2009/07/31 End of July + Expenses:Books $70.00 + Liabilities:Cards $70.00 + Assets:Cash + +2009/08/01 August + Expenses:Books $80.00 + Liabilities:Cards $80.00 + Assets:Cash + +2009/08/31 End of August + Expenses:Books $80.00 + Liabilities:Cards $80.00 + Assets:Cash + +2009/09/01 September + Expenses:Books $90.00 + Liabilities:Cards $90.00 + Assets:Cash + +2009/09/30 End of September + Expenses:Books $90.00 + Liabilities:Cards $90.00 + Assets:Cash + +2009/10/01 October + Expenses:Books $100.00 + Liabilities:Cards $100.00 + Assets:Cash + +2009/10/31 End of October + Expenses:Books $100.00 + Liabilities:Cards $100.00 + Assets:Cash + +2009/11/01 November + Expenses:Books $110.00 + Liabilities:Cards $110.00 + Assets:Cash + +2009/11/30 End of November + Expenses:Books $110.00 + Liabilities:Cards $110.00 + Assets:Cash + +2009/12/01 December + Expenses:Books $120.00 + Liabilities:Cards $120.00 + Assets:Cash + +2009/12/31 End of December + Expenses:Books $120.00 + Liabilities:Cards $120.00 + Assets:Cash +>>>1 +08-Jan-01 January Expenses:Books $10.00 $10.00 + Liabilities:Cards $10.00 $20.00 + Assets:Cash $-20.00 0 +08-Jan-31 End of January Expenses:Books $10.00 $10.00 + Liabilities:Cards $10.00 $20.00 + Assets:Cash $-20.00 0 +>>>2 +=== 0 +reg -p "monthly january 2008" +>>>1 +08-Jan-01 - 08-Jan-31 Assets:Cash $-40.00 $-40.00 + Expenses:Books $20.00 $-20.00 + Liabilities:Cards $20.00 0 +>>>2 +=== 0 +reg -p "weekly january 2008" +>>>1 +08-Jan-01 - 08-Jan-05 Assets:Cash $-20.00 $-20.00 + Expenses:Books $10.00 $-10.00 + Liabilities:Cards $10.00 0 +08-Jan-29 - 08-Jan-31 Assets:Cash $-20.00 $-20.00 + Expenses:Books $10.00 $-10.00 + Liabilities:Cards $10.00 0 +>>>2 +=== 0 +reg -p "yearly 2008" +>>>1 +08-Jan-01 - 08-Dec-31 Assets:Cash $-3120.00 $-3120.00 + Expenses:Books $1560.00 $-1560.00 + Liabilities:Cards $1560.00 0 +>>>2 +=== 0 +reg -p "from 2009/11/01" +>>>1 +09-Nov-01 November Expenses:Books $110.00 $110.00 + Liabilities:Cards $110.00 $220.00 + Assets:Cash $-220.00 0 +09-Nov-30 End of November Expenses:Books $110.00 $110.00 + Liabilities:Cards $110.00 $220.00 + Assets:Cash $-220.00 0 +09-Dec-01 December Expenses:Books $120.00 $120.00 + Liabilities:Cards $120.00 $240.00 + Assets:Cash $-240.00 0 +09-Dec-31 End of December Expenses:Books $120.00 $120.00 + Liabilities:Cards $120.00 $240.00 + Assets:Cash $-240.00 0 +>>>2 +=== 0 diff --git a/test/baseline/opt-plot-amount-format.test b/test/baseline/opt-plot-amount-format.test new file mode 100644 index 00000000..9c5484af --- /dev/null +++ b/test/baseline/opt-plot-amount-format.test @@ -0,0 +1,10 @@ +reg -j --plot-amount-format='X %(format_date(date, "%Y-%m-%d")) Y %(quantity(scrub(display_amount)))\n' +<<< +2007/02/02 RD VMMXX + Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 + Income:Dividends:Vanguard:VMMXX $-0.35 +>>>1 +X 2007-02-02 Y 0.35 +X 2007-02-02 Y -0.35 +>>>2 +=== 0 diff --git a/test/baseline/opt-plot-total-format.test b/test/baseline/opt-plot-total-format.test new file mode 100644 index 00000000..1065c5ce --- /dev/null +++ b/test/baseline/opt-plot-total-format.test @@ -0,0 +1,10 @@ +reg -J --plot-total-format='X %(format_date(date, "%Y-%m-%d")) Y %(quantity(scrub(display_amount)))\n' +<<< +2007/02/02 RD VMMXX + Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 + Income:Dividends:Vanguard:VMMXX $-0.35 +>>>1 +X 2007-02-02 Y 0.35 +X 2007-02-02 Y -0.35 +>>>2 +=== 0 diff --git a/test/baseline/opt-price.test b/test/baseline/opt-price.test new file mode 100644 index 00000000..06cc7751 --- /dev/null +++ b/test/baseline/opt-price.test @@ -0,0 +1,47 @@ +reg equities +<<< +2008/01/01 * Purchase Apple shares + Equities 1000 AAPL @ $2 + Cash $-2000 + +2008/06/30 * Sell some Apple shares + Cash $1250 + Equities -500 AAPL {$2} @ $2.50 + Income:Gains $-250 + +P 2008/10/01 02:18:02 AAPL $3 +P 2009/01/31 02:18:02 AAPL $4 +P 3000/01/01 02:18:02 APPL $100 +>>>1 +08-Jan-01 Purchase Apple shares Equities 1000 AAPL 1000 AAPL +08-Jun-30 Sell some Apple sha.. Equities -500 AAPL 500 AAPL +>>>2 +=== 0 +reg -B equities +>>>1 +08-Jan-01 Purchase Apple shares Equities $2000 $2000 +08-Jun-30 Sell some Apple sha.. Equities $-1250 $750 +>>>2 +=== 0 +reg --end 2009/06/26 -V equities +>>>1 +08-Jan-01 Purchase Apple shares Equities $2000 $2000 +08-Jun-30 Commodities revalued <Revalued> $500 $2500 +08-Jun-30 Sell some Apple sha.. Equities $-1250 $1250 +09-Jun-26 Commodities revalued <Revalued> $750 $2000 +>>>2 +=== 0 +reg --end 2009/06/26 -G equities +>>>1 +08-Jan-01 Purchase Apple shares Equities 0 0 +08-Jun-30 Commodities revalued <Revalued> $500 $500 +08-Jun-30 Sell some Apple sha.. Equities 0 $500 +09-Jun-26 Commodities revalued <Revalued> $750 $1250 +>>>2 +=== 0 +reg -I equities +>>>1 +08-Jan-01 Purchase Apple shares Equities $2000 $2000 +08-Jun-30 Sell some Apple sha.. Equities $-1000 $1000 +>>>2 +=== 0 diff --git a/test/baseline/opt-prices-format.test b/test/baseline/opt-prices-format.test new file mode 100644 index 00000000..ec93d9cd --- /dev/null +++ b/test/baseline/opt-prices-format.test @@ -0,0 +1,19 @@ +prices --prices-format='%(datetime) %(scrub(display_amount))\n' +<<< +P 2009/01/01 13:30:00 AAPL $10.00 +P 2009/01/01 14:30:00 AAPL $20.00 +P 2009/01/01 15:30:00 AAPL $30.00 +P 2009/01/01 16:30:00 AAPL $40.00 +P 2009/02/01 17:30:00 AAPL $50.00 + +2009/03/01 Purchase + Assets:Brokerage 100 AAPL + Income +>>>1 +09-Jan-01 13:30:00 $10.00 +09-Jan-01 14:30:00 $20.00 +09-Jan-01 15:30:00 $30.00 +09-Jan-01 16:30:00 $40.00 +09-Feb-01 17:30:00 $50.00 +>>>2 +=== 0 diff --git a/test/baseline/opt-pricesdb-format.test b/test/baseline/opt-pricesdb-format.test new file mode 100644 index 00000000..28998903 --- /dev/null +++ b/test/baseline/opt-pricesdb-format.test @@ -0,0 +1,19 @@ +pricesdb --pricesdb-format='P %(date) %(scrub(display_amount))\n' +<<< +P 2009/01/01 13:30:00 AAPL $10.00 +P 2009/01/01 14:30:00 AAPL $20.00 +P 2009/01/01 15:30:00 AAPL $30.00 +P 2009/01/01 16:30:00 AAPL $40.00 +P 2009/02/01 17:30:00 AAPL $50.00 + +2009/03/01 Purchase + Assets:Brokerage 100 AAPL + Income +>>>1 +P 09-Jan-01 $10.00 +P 09-Jan-01 $20.00 +P 09-Jan-01 $30.00 +P 09-Jan-01 $40.00 +P 09-Feb-01 $50.00 +>>>2 +=== 0 diff --git a/test/baseline/opt-print-format.test b/test/baseline/opt-print-format.test new file mode 100644 index 00000000..103ceb1e --- /dev/null +++ b/test/baseline/opt-print-format.test @@ -0,0 +1,10 @@ +print --print-format='%(amount)\n' +<<< +2007/02/02 RD VMMXX + Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 + Income:Dividends:Vanguard:VMMXX $-0.35 +>>>1 +0.350 VMMXX {$1.00} [2007/02/02] +$-0.35 +>>>2 +=== 0 diff --git a/test/baseline/opt-quantity.test b/test/baseline/opt-quantity.test new file mode 100644 index 00000000..5de92e84 --- /dev/null +++ b/test/baseline/opt-quantity.test @@ -0,0 +1,11 @@ +reg --quantity +<<< +2007/02/02 RD VMMXX + Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 + Income:Dividends:Vanguard:VMMXX $-0.35 +>>>1 +07-Feb-02 RD VMMXX As:In:Vanguard:VMMXX 0.350 VMMXX 0.350 VMMXX + In:Di:Vanguard:VMMXX $-0.35 $-0.35 + 0.350 VMMXX +>>>2 +=== 0 diff --git a/test/baseline/opt-real.test b/test/baseline/opt-real.test new file mode 100644 index 00000000..b406f29a --- /dev/null +++ b/test/baseline/opt-real.test @@ -0,0 +1,11 @@ +reg --real +<<< +2008/01/01 January + Expenses:Books $10.00 + (Liabilities:Cards) $10.00 + Assets:Cash +>>>1 +08-Jan-01 January Expenses:Books $10.00 $10.00 + Assets:Cash $-10.00 0 +>>>2 +=== 0 diff --git a/test/baseline/opt-register-format.test b/test/baseline/opt-register-format.test new file mode 100644 index 00000000..2384b6e2 --- /dev/null +++ b/test/baseline/opt-register-format.test @@ -0,0 +1,10 @@ +reg --register-format='%(amount)\n' +<<< +2007/02/02 RD VMMXX + Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 + Income:Dividends:Vanguard:VMMXX $-0.35 +>>>1 +0.350 VMMXX {$1.00} [2007/02/02] +$-0.35 +>>>2 +=== 0 diff --git a/test/baseline/opt-related-all.test b/test/baseline/opt-related-all.test new file mode 100644 index 00000000..645f8a91 --- /dev/null +++ b/test/baseline/opt-related-all.test @@ -0,0 +1,14 @@ +reg --related-all credit +<<< +2009/06/18 This is a Test + Expenses:Food $20.00 + Expenses:Tips $2.00 + Expenses:Tax $3.00 + Liabilities:Credit +>>>1 +09-Jun-18 This is a Test Expenses:Food $20.00 $20.00 + Expenses:Tips $2.00 $22.00 + Expenses:Tax $3.00 $25.00 + Liabilities:Credit $-25.00 0 +>>>2 +=== 0 diff --git a/test/baseline/opt-related.test b/test/baseline/opt-related.test new file mode 100644 index 00000000..576bc9d2 --- /dev/null +++ b/test/baseline/opt-related.test @@ -0,0 +1,13 @@ +reg --related credit +<<< +2009/06/18 This is a Test + Expenses:Food $20.00 + Expenses:Tips $2.00 + Expenses:Tax $3.00 + Liabilities:Credit +>>>1 +09-Jun-18 This is a Test Expenses:Food $20.00 $20.00 + Expenses:Tips $2.00 $22.00 + Expenses:Tax $3.00 $25.00 +>>>2 +=== 0 diff --git a/test/baseline/opt-seed.test b/test/baseline/opt-seed.test new file mode 100644 index 00000000..48ca7d13 --- /dev/null +++ b/test/baseline/opt-seed.test @@ -0,0 +1,432 @@ +generate --seed=10 +<<< +>>>1 +2186/12/04=1987/04/07 (gxB) Xcm0wJS7fGh07 + ; K93x8n5028OV19:n9zv0ksC + [MIdo:Y377U9xhRjxQaFFk37:aEaw66xn:9ss914] 411126pQ @ ozgYxo0.578679 + ; n1:1Bm38PMy6FO51R58pWhXmc1HN08Umw8T + (X:S) -936200r @ nijv235713 + ; j7zUn1U8UPStcn46 72dpS5vd9d2oxQk:R + C:27lps3E + +2186/12/09=1987/04/08 (B7xDea) yA6fOo:q0A6IaA7v ; LB:l + cro FhdXRF:nROGw1H8PqY10Jg 3FQRrcX07 -733399YmHWf @ 0.640803LMtLP ; dVmRkOR + [Z2t:31I:2ox:81NC28A0] 263844DKa ; h4I8M355 + B0VD1MI2073MblRrsPV64O9 EEMQ 816045 @ 0.499731hzxnl ; qAsYFCY + [b1X09LHz:AkK5G] U -785951 + ; gJy3hZ4g75vf84:9Rz R 6Vpy11ZJ:24K m U:Y + DI7 3 ; H64 + +2186/12/11=1987/04/12 ! (Xu:Eo) Cx4o2an 3Upa0Fll:nDjT461:y ; uzZ5uQlLWR4 + [XKU2Jj6:p6I5kVSPv 3vpG:ShyKRF:dv8y] Tgojw -14862.9 + [SPOiCDS5Zg:t:Q7vMF01n8P:7h1i] qXxf 437098 @ BmJ866903 + [reGXB0LK38iD5FM zWxl PUW:eI5F21EN80e] acLq947715 + (NA LA 11uhxP8y7oBBgXZQ45lwPb1) 14641.5sndK @ 44.2184885 Cw + ; P4158:i1xa1eoHUHeF1pS8 + [CMs:8Yz9t7H1aF3qN] qt250589 + (B0T) a-715047 @ 0.946357 Ac + ; PeUU8DYH3 BxS Uu9y:YfU0isX6ly:sZO6S2xpN + V:60vuV + +2186/12/12=1987/04/14 * (OEWOu) SLGS + ; AuFLS1XOW:z04:gwQ2 + (VjI Tm79d6) -93666.5xuIH ; A9 + [UF3xXu kOA ERwiQOh aprUp9gM] -103361jSZeo @ 564002 iJOXN + (EzJ 65T295) QCaR134452 @ RuLEEw 0.6292982 + ; M8916aJx5C7:G9U0I:y4t68 + W oUJuq66YfULC1 CW95nX2VZch2:qK992pM3fhy manJj -209990 @ c 536098 + ; yzOvW 28W99zgmW9Y9:j:5w:1:gIX + rNUT0rM:rBsW:P87ZVn3d8X + +2186/12/17=1987/04/15 * (F7u) sZ:c vZ8U3Sr705 + T2DT75mS9x7 W727197 @ 1.264289dfiel + (Z X9E8W16j8:521) zPFS-334854 + ; RkIZ27O30b8:yf0pXU + (jAmK) 426564AO @ SemS 421251 + ER lq6N3 q 3317.11 @ RAAii 254.77207569 + ixS + +2186/12/21 * (EVhm) fnA 7GRR710:8:FhSlZRc:4L5J74Q RnH9H4v ; N2 + (dGg77g:i40RE7J3KnxNNHB0E 7) 262042 z + ; N3V98V827I:qaJ2U4E5IR tpHJw0J699:f + M8:vCvoagZmVL1Y:543:eucH98:Q 222936 aZtSl @ 1.851056rQWi + ; Os2Z7rnOU11Y9vm8pGNvTuK + [KWybHT:s] Zc -395512 @ 673430 Q + ; JDK FpB 6g 5dBDa + [Tk:B1257JrD9:LgqbzmH8GQ] UaS-222545 ; uiM6 + u335Ic + +2186/12/27 ! (GS) EJ99iD7cHek7EJp5aoFYIXz2Wx:hTSIPWjP + [rRlyn:21:Jcu292Ea] YuOJ-77111.5 @ ys9.708928 + ; EI309:2D t66H6NzNv c AXmMzeF4:kR + [jn 58DWi8DwD48Ee3:05qkh38Y8754VMaC80OD] 205960LdQ @ iiB 2.206851 ; vTJ9I oLQ9KF1l + [zqBE2yO1UWmgM4BL:A9Do4EffRd8V:io] w-53161.6 @ 350764 UclkLw + g:R000qI3C 746879 juFf @ hlN448555 + ; x5:1OGWm WY9Fdtu:8rD09k1:5z 1:8Y:xQ11I + A43J7Yjx6xKhN0:TjP ; f:l8Q 1L7RjfBQ + +2186/12/29 * (R3:UJ) hN K3YTd:0y:42Ze9ObAaT lgx:yJ:ir1j ; xIIL05IE2p3 + [y jLigz uChQA Eg4UEf0 70B:94:i4DqcQq] iyg895724 @ 0.510892 mCiSp + w8C 7Hx8Rq91QS6z:7b -331711 vDTIl + U0987:F:4DTh6V F6 Rkp3p1 CTDATK-782095 + [kG7R6F:9jPxChWVOObKg045AE80hc87FW] 252408ViNkjs + jHM I5:Y + +2187/01/02=1987/04/20 (oCi) coD:3Y1ZEF79r:C086 + ; q:Gk:331:S9 o:0CREm + (bML5K:jl7ntsH213:Q:1BjWS8Lrqp:3 qSg2 93) SgXoC 338602 + [mr7R wS405TMYlnq7:Jx:e7:Qv8uElrM01Ww] 596325 IEVnen + ; V220WkzjO p:eVRrzBl473Nht:26U + o1h:E + ; n:34:ThQmLaTUfgbrNm:71p1C9 + +2187/01/03=1987/04/24 * (e) S:8O8F8P4672h6 ; UEJwkn + (iA:R70:9:Wzp4x:v9skm) SXV 822239 + o6Vo3X:HmQ:8s94RFZ8:fbxJYY2:xy6 wE 129507 @ 7.102689 hq + ; T:Dr0SwdlwU:3gn49 + TDM8vS3 4:l497yk2f2 d307311 @ 0.344104 xOqmt + ; hrZn9 5BaTBOkT16 y:65f848liI558:f9 + ADHTI7b:417f9NC p:81Dn:dd:2t58QPvkr6 280677 ewaJgw @ OhuZ248193 ; US178U5:E2P + JvY8mjt0 p1F ; Er nO + +2187/01/09 (u) I55B7BN7Nic0Xf7l:enK6:wZEp + [u7n1K7hy3kMKbPG hL] pIYM -959467 + [Xe3P2P:oUoMA1slU09R] -544505SfH @ 852228 OkXknF + ; z uE9:xQ53Sy0 s3p53voVFV:bOP2mL9K4hBf:24 + YJ5N:el1lEP1rksmQ4af8vXzlix:87 oXtoLV-494724 + [f28 6:EG5sn 6clN E:689EhZ6rljIS9A05HB] -592178TcaIqX @ 0.52632KqgxJY + oiMz51lFxbnN + +2187/01/10 * (f:YKS) b152q + ; l0728x5Ep97f405oy5sTr4j + (X2l:yjc3P5Jf0R8slv7fn:DWa0HB63yUBY) -637008UW + b -526513qGuvC + (P9QJnILJLlK20j9) VJx -237745 + ; H0:S1m6o:kpqGBmr0jn4yGp0PPx63680Bo + [JpVMi82HvZ8Cjn8R] jVSSg-913619 @ 0.808966 yoc + ; jYk3X58298IFBV7M TH3WN4n l0:x73agt4z + (HU 0M:e3A:030XdN3:8g) 243401kRXr @ eW 346761 + ; V6tnx1GMt6B0iT5DL9 + Eh EjQL:5Rj:68NZ42kd 16djH k883779 @ 652143 KQK + hlxM319I2412y + +2187/01/14 (p7NK) i56 + (E0V bMA) -901145n + ; I0G5G9axvs32G0R19D72Nj + c:6q9 dM:73:5Nc1p5g:c0 q -764271.00 @ 0.310259Pwv + [Nf2:jSoF:X79mOgTa:t4 CC8903Ug 0cf Gv4w] -345268RzzzmL @ e 1.692647 + ; R:KT0Y:WIkiD3UB2Fu6:CK5X6G6K1 + (d) PDGe 828999 + ; iFk6G5XkFII6739 31Dq2Qde25a:vfvRL0 + NDODeT1ud913r9L:s7JDMkkGwNHEoac ; FyMDFsNG + +2187/01/20=1987/04/29 ! (uvZ) C79:n5SB4r + OV35363j:REM:JjkW -996837ufeCSQ @ 0.47472dq ; EqA:SE2QJpd20a + [JTaWorK:018RpRg1y23Eh2FzS:o84BimcBP] 644258 VmNxi + uh:l8fPO05YCYi053 + +2187/01/23 * (M) p4yw23Z7z708H2t 1d108W509F XtQHd + ; JrdATcORmZ7F4:RilGAo4nPERSla K8:q319 + (pgNXjW:7:w c6434RmZg9x39A QgUoDd8NZ) SUO 203170 + [ob] DHAra-72498.6 @ 2.7968954yeCD + ; xTxkz:Qo:27 6146k0 + [o:1wP7Q2] 77384 dQolH @ 492890 l ; H:d6 Yu + [vhjm6FF] -597065 iBP @ brzs0.0717997 + B1V:H4agb9:aLP9235e2b + +2187/01/29 (KQhu) Q7x65f8ls25W88oyPR1H56I + (lX 0:3ZQ8e:f:We0b4lAUvFl7lf5x208) 383456kQrwsT + ; wa0f6v8ZJ7:on7T + [She:qWK1Qaijt7B5ua0] kniumo499909 + ; Z:CP:A5hK5uN4JI7FMqS3V:vyVS2 + [Y68R:A5N] 798458a + ; x:26gTlXF1:7:R6BMewcn:H:N8UXE + (QfdF:4GLhMy) b-547602 + ; b aPGwAfp Qx:m952ctx495 + [utb8nOU0r9N] mLHbE223101 + A6f1R:zEq0Vea3gsKnC9:06 a224 ZmNQ 774115 @ 892757 OQv + ; X6g:0Ka:8E735uVV + W6zw11QYbiE3awi1d A + ; pXFvBnAu8E77qcw:ZUlwkWV5lZ:VkC3:5gZyaERW + +2187/02/01=1987/05/01 ! (wv5i) f6n2pmhOP77oyv6:IG5j0TEk9v5pXP9810s + V 5w Po:lVghM1g4t3BXKy B B:1Zz gT -123047 + S3j:Aj4s 791849I + [B9:WfB9KIt4vLU51:Jm99I6Q125dMRIqx66vC5z2] 242497 A @ 583994 uXRfdk ; p5H8:j:o:fES + lS7d:q -106717 iefO + sj + +2187/02/04 (s) Z1yWLuX5yE4h:xT0X0Ga:o7:8T:0BbAvP46 + ; X79u5:K mYCw9Utl + (uE:h:1jzdLirp r gP5Z:AF:79I) -711627oRDqJP @ CLpS1.184335 ; DUx7WX5j4nX + [t2xBfs53KgY vP:XxCk2bmJC92 3Sx5U4] 313034 GINL + ; lLpf64qmRAVY7J943jaE:c:l 4570l4:P + u8 84Cpd e3 + ; g a m05HNW45pto sn0pJPv0U9r5bfT + +2187/02/07 (aj:5) X7rkc3PjvxTL l + V8aoGZ8p IL 50587.5vBeRJ + M6Ai3R:Q 325120 O + J5EUo35CK:qrg4E:V RznCQB3:q8MX1tX0CDyp 975643CDIcM @ 855572Twu ; bf5 A + [TM5Z] 913463 oimIl + YI8S9:X:C6zA 75 + +2187/02/08=1987/05/03 (u5N2W) X2Lbql6n O:z2U1WB0 iDB8N0:4O286Be4h5q5 + [q95j7F] Z -886734 @ 931353 bNt ; u0G KuFLh nq68 + (Y:3LOBz1:S:nA7B) wwL-880685 @ 960618G + X9:gud7uR5Qxth7:f1Q0 + ; F2lUncNAoUU4hWY DVe + +2187/02/10 * (E) n07K0X69w9Lm5n:Kas2ErCM6l4 48cR18Y:SnV + [R Cv H:6SpHO6:l63Gq:20JXImfc] -667443 y ; Kv + [mI9ZfM02qNaD8] 668500 RYSLl @ ddu0.19124 + d:BPcI1lYVW9CcA6yL:4lhaK6ID717 + ; N8Ix1H95urPX pLL5m:vz1mg:e A8 + +2187/02/13=1987/05/05 ! (c) Bb7b10:fG8C 6 6Wx1l + [Ef] -85775.1dN @ 315212ejCU + (wLjq:0601Iqx:96 HF) 658188HKt + suu2OqWMY6sJFv:xm:Tx -638358 jnDT + [g:9S f8OoiA87l0I4Kqx3] -601167p @ 43014.8 aLq + ; S:2T:i:IzbT:bNP5snh:2Pk:5nu + lN 8U973P3983pe32X:A:219N:1 + ; W:0 BO yy8:w8evC7YmboP ghx2:J9Xo + +2187/02/18 * (AN) oUwsLl:0Mm6p66d4leuup0:2x + ; c 5kp7U8G1k4:3qemxEP9IIYN8YUxA:8I9q + ysc8Prp:BAO3:NmJPIpZli -863446K @ VQ568439 ; tA6 + (I 4u8PI1L81v:aOP1QE74d 1DDZBY e7) Xl -571100 + ; C3SV V0zIpW0K6O:Uy1d:Q7dvwz:o5B EQ Nh + (L bD:5l2K650y8i9Ja) d-942453 + ; FjCMw2cXC3T5S1 P7d:d:O63BwR6O + (qYLNQ:ApC) P -371484 @ WLbB 716482 + ; LkfG:wU7kt 6Ayo9H9 ho + (AH7Z266:lo9126ooP:ok6IqMf0) -695058SXInq @ Abz 913865 + ; Lqh76O:sSk3Y9zRR + [f:z:n2:bO7fwc230R:2Y7fjX9Yqq:649] RkqnLo 249887 @ vTD 1.7248 + IW0M187 Ub + ; u4h0r2cSYS1kHW 9j6FyRk1y + +2187/02/20=1987/05/06 (W95) k + [TDrC8nTiD:r4Dx9177sQJvd16Ed9SvU mrE81:oD] bipfJi -629380 + [cD2:Hy:DV6zL a647Aq7Pp71gTruf:0] BzPmpg 612835 + ; W3FiFcDe53HIm789:kQN + (nP6CPmJd9g7jS72M 41q:t V5ltrdUx169Zu28) 975397 shihH + qT7c6 -655081 BXPPc @ Ya 872226 ; yIX7OGx + (mQSZ) 426510 zcBER @ tsnr606449 ; cJS:z12J4QvOA + [Yjfk:FmxjrVAa278WBuv5:50eG7w] Xake56556.8 + uM61VJYWHmmS2swhTs1624firiH7MjO5Z5 M0Y + +2187/02/23=1987/05/08 ! (R) Dg8:279q:90GKH7:pq3WN kXi:C:4qf76 + ; NlnAoFwM C7d3KH8A:6:lHr o8kxh6N130jo:kJ + VI6YZ -597872tIjW + (e:81C4:oO swE5f 9l:d9) wUna-360797 ; mJ y8mI + (a2y 6:iZLCdeW t10vqB7n3YN:7) 272995 NcKRN + ; taqP:G6i 8ePbU7GD59:r6iS + E46j:t7f50rjzczn -420867In + (qfK1A qE9SAMwwY:RbfPDS) lAcJ136457 ; AJ7hu5lhG2Q + m9GnlWF:P 2e6300i0o:K 930528Om @ 127927Rq + ; YN86XuS17Ax6fBYw8:FZB + v89c7V90x898JVnHFV:i28l3mMf1xTU:Bo7iJ2d ; YHqY + +2187/02/24=1987/05/12 * (t87o) mjrM4MyN8oKdpS4s + ; dh4gFZgBk5z29g2F44zT5I9g58Xpo:AYe9u3 + (uQI843zXpD9vK6l2dc93) VDZfau695674 @ 1.395381tJ + A:UT:2t 4BPcec8xuFg Qb2sRg79Wt -715837BWwCJu + i uRN1hvI8 + ; Yh 2:snU9F5u0Q7:eT507nI3mN oSE8MkeZ0kPnF + +2187/03/02 (JaP) M:j9:DzCYyd:M V6 ; YCd5tl8gTY7r + KWu6Q vju54Wqg:Hajy3K5 699037PsAu @ 0.0432019 dmmsRU + gm:9:Uf:t:jZCaY8ZoPqd UB:qjC5OtR v2y -65372.2DRpj @ aPXbpG0.66845387 + ; HOntIQNDiUq2h7O88 + Lq6 + ; hB1 YKJgE pnE0a + +2187/03/08=1987/05/16 (fVUT) GB0y15 ; gw8ge764c35f + [fgY:5j4fG6:uBT] 994884 IuSb + [Qv84kw0f1cxt8MBl] -314694 SKN @ uIGy0.488405 + ; S x865af7EOVbh09z1C:atG + C:aOmIU 6quv27:L8:fO0X61IFuzPKm + ; cU95Tz:wN9F99u2C:7QW2R6QdK:y:5Lcdf + +2187/03/11=1987/05/18 * (Q2P) kOT0:08TY + r19iHviTP MQmbBklzoe5Zy:8T4 -701612X @ 1.347401 dhCZ + j8hn 8a:zJvx -950080X ; X6V4:Vrz7:A9 + ChMhD kwW2Drf8oW4 m23b6 rg17K cc + +2187/03/16 * (tZ) U C44:BGa4J6Bzwwb4o + [YK49QGHo:U167ZuH:Sus] -653997 r @ 990828 WsMH + ; YY7Vl4m0y6h:97Eb:t:WZ38 + [rkd6eQ QnFRO4Eap1:o24ze7 MJta:5Lmw8y1] 713018 dOybc @ 867696 pqosoj + L rGB fhmmAj41N:75MlS:HI02Dhtvv92:9 + +2187/03/19=1987/05/20 * (C) x4MFm08t8:Rv484 + ; pteIOL8G3L7K:PiF:ed01NaE:5 + jLXaJ7:TPkx:hnc0a84T949V -337155Pt + [z Cimq6yQ5 6Hz:4:y j3pTBDWr:Xwi5xa4fT0] egJUlL927254 @ ETLYp 830216 + ; i5s0qZi5:it:qv1g 35vrb4GY:4y5 + (aGO8o973:8:DqP588) cReiuY -813613 @ ZVwoPf 932860 + [iG0OQ:tWl:Y:F59 R09pKiI] 499724 y @ qwy 1.437329 + ; bNO myx5K3DhtxI0gZGc4eWCtZ:Nsb:l7Yx 3v + [OT2sQn:78mn:6dC6k2BPLg8Xu:1NV8GHg6cq9d] yFpTo -290498 @ PdADxe 2.729485 + W2C:PzB:a7iA:VHeXq7gYKI vM2611:bCoq aHgK 381854 @ 721958RKv + pPYXx2wrFvMC:94zr3C9T1TdhR6D4 + +2187/03/25 * (R) Z6et66o5F44:HG74D5gdk9 + ; I8k7lSGq5223eFvFeSX4B20O + EiOxe -49170.3ptFX @ WYtmyT 781596 + [cn U0zGj:Ylm2R:4a Sy] pxLB-155187 + x:yw8duO9Ic:4CqN:AUa xd + +2187/03/27 * (tg) VirzaV9213J8:jkhY7b + (Kk:22T6893q129HLfMQ9QZf K66Kc:1 Qu:51Ki) hEP288640 @ 1.734694 ES + [yP7cI1x:Ns7Ik:CF 8W:2r:tT8r:ND3K4naIL] vnCq-270931 @ 368422 Qum + [OkJ:WvF64D87M22b58xgnAS2yf85] -542640 eM + [P U] 602501 oUB + c32Ww93:Z:mj7BK + +2187/03/28=1987/05/21 ! (kIWM) lz L5 + (HoA7y0U4v4R458U80K0wqj4V8L:3Hy hK:u2o) baf 692841 + (tX) -864521 x @ Vy0.360209 ; Wxe043p9:061 + (Lq:5g7L:wwv:HyHFPCZYy) MYpd631128 @ 508656Cz + (x5 79ER 2Z3I0U19 47VfLtNKOq:2X) -696823bSC ; a8:Ad6LdI + +2187/03/29=1987/05/24 * (O) E0Rfh34 + t4FdLx3:l:Ck6m3VJ stAj-908528 + ; vQ8h:OiGS:f7CG 5 Y7fBz d10 + z 2936srygkDWhn1c4jyG:L:nHpTrnLB51 vG75178.3 + ; UfL7dG5a87Mm430vKtQ4Z:M2sdMzh3:a + [bdA:M:AM9FCXQm6HMGIB7 Lz:YV4tm3:r:SN] 38780.8HvaL @ HamR 19.0182771 + ; z4KY C:Q:3h2 X 7 + (R:n9:lXU) YTx-417744 @ 1.230378 D + [DLs 9 mtV 1G:b:Bep yVU n93Hbd:8By2Mx:M] -48707.2 a + [R:Goz1E0Bj:blF5Hl] -298794rPVm @ 3.283965 bUoyr + ; KYa8M9:6482B650mXUE7b:Ka + w8NK7U54q7vHGU0l30t:Il:3P:U T:SfY0 + +2187/04/01=1987/05/26 ! (Eq) M59Ml:3 + K ASPeZQ:7LW:wzN2QaUr77OXr wS-848544 @ Exd 0.625834 + (tX0 1Uhq2Q969Z5x07I4B:8:n2t4T4V:E:6K) rQem -637441 ; ErSB:G0z + RqXG2 IZT646tE30:n84Y fEt-767950 @ 0.594623 Hkqq + ; pw3k2j 4KT D3Uo:B27e:WKDiuOD43L + [C3] BheR-157970 + (A8p:062sBPYe8e:6oK:n 8X:87Qn) 982770 uVsEo + ; oHE U:F7z1f1b8mTv:s 2KwNo7 WgI + [D20EU:1] Sppd 837068 + Bdn0znI799:07:s60 M5099z7 + ; dEGfxH:U8p6z8DBSZ:diiQ49H21 6 + +2187/04/04 * (M80a) Z:k206 + [x7:anc] 437316 Dqk @ eZCzj 0.285032 + [hC705 NtL:iP:q3608P93d7P:GdE] 544670li + ; oy:4zAY3upE31j0VUKCo95b84:O9E5d365E:4 + ysN:P:56bd43Z:v:krkEyf R y287D + ; eZ U0AU4:MSGD:jjK7O94rD3webUWW1:4r0:07 + +2187/04/07 (vvA) wc05O32W:H34:Y990 + jBCX1J524A63U1k1j98823 2PYK886n 391730qA + tf50 ptOxzto:d6O0ye:cg8MIIR9C3ZlTRQ1 nx920305 @ 1.019439 MyVL + (UaTIt08Xl47:98zv83X92k3Ap) nlfkS 154433 + (j3M44pR2dc4J:rTP7oN 2kO5 GOp:2zo8I) PXO-126823 + ; yM21414i0eV2HAu50r61:5WN:2ApRhE:sM:ic + Xs551d 14OR5:j:5:u + ; psjmtc 57 BvF44CTmU7BXYna9w:2e58Wk5dV + +2187/04/12 * (Rh2) xe:4694A4vNXtTO:BRdX4n:U:6gLXW4atziLPi + h:E31A:6lSbJKE181aNzg2l2s1q -158224QX @ dKAD 4.682267 + (pfitL560:H:rL2O7L) 976398 wMZ @ t486509 + Motxoa 3u1 D 58373.9 V @ 3.1142171 a + ; tnBH6o 757IL 3:bche8 5 V:6 F29 + [U 76 7PO6E2M6fJ47pk3no K1KI6r] 690776 cL + ; Tz1W zMl:6r0Sr67wwiv6mHI1:UMowDl + (A:4s97lX2ZJg5XL:JASmf:H) wTEdPr 126890 + ; E8N231t1v62wd53wZ3S7ZJ + [MkN6f0006:53MX36298W vL2Rr] 131653voz + i:Y82PTu8o0RsU5c393 ; m09zvwT873z02 + +2187/04/16 * (O) g09nmf1f ; fiQ T4D2:E + pNwv4w va614303 @ BfEt776882 + [z 8M3qc:0W6 323:7 HE46vE5goXpC4C] GZatQD349279 ; c79 + N6Ny7D0NikU2Qzy:W:N4M:94F7D:C zua3Gr ol-473902 @ TiG1.58947 + ; USUm8 I01T7:rL9xC4H6QaI1fI4zg4iw9D9D94NY + iK9PGH2Lr:5xL 11077X ; WiNki + QxGAnH:0i1:5:hpK1454yx + +2187/04/18=1987/05/27 (d6 76) WKycS8BB4k2pWB6U1 TqC799Q3Q:Q:uvDbP v + ; S1 H:53Fz:Z9RY7660oS2:iEzlx8zId1KHlAp13 + WAIZL7YC8T6xsxb C lEBkQZcC6ZYX8oUdjs aBsqCO-859192 ; X + (dVeDho1zLvBM1:bc2907v rw6w:fIyj) rJO-348003 @ 0.5828lNOAP + [VYR86aOPCBtU:T43s31qNrcmX55] 148376Kaqj + ; Hb5Mz2T33FTGp:6mf92L1 + g0P FOnuBD634440 + Eu2590k:6 542890 YbTkPd + ; MG7:fX1QN7F:aBWcZ WK:miVS9IS4 + TXhdCnW:q3 83Q0:A:TLa:P1 389207Qu @ ZxD 474844 + ; ze Xr:DiUmY4O5a494 E 7C jd9f:y + ej:OV1p + ; UQIvt cFi75q3l7:33q9V4epmq1c3o:MQY9:4 + +2187/04/19=1987/05/29 * (yIEGx) vtu yZ:FLNg8 bz81244o 1 + [eQ0:vQFftKq312 5:qt4OJRF14:8iUS9] -514867H + [zS4kj:1U364n 77yT4heLR0WCtP:J:qpTVC] C 626719 + ; k61HYqVgh4pw4PSJ:8Zd5c0LvKhEqop + hQu J5:iL7i:dlczgv5 aUXZ:SW20:d + ; B6CQoX:m7:T0DObHKf:c35E tVQg:6N7 XR0yk1F + +2187/04/25 (d5) mO3V65tcQ93f:bpH2O4KQc7i8A3ubo64L + [cY8j:X:D1g53O] 913440 AkznZ + ; qiWp0:1K:9:yoagiids8YU8GazTJnDZyJMuJjU + [h 6v3:GS9 8] w 909579.0 @ 0.439321 a + QpWryY4 + +2187/04/26 * (Oa X8v) nTu ; Blnf + RcSi7T4:bA87Zk v1a6Bue2RiF3Q:1vHkrC Yd-594954 @ 379530 uXzDxT + (jlB2E47ka6U7mwZ3Tp:56wE:w Gr5pco7WO) MJ -210135 + (HrjMwB92A2:4:bnLz1hf5EAIx8wo71 AnSM83) PzifkG-18466.2 @ 532525.0 a + a E913Z48r PEZ930954 @ TWN0.26211 + K4:3w6:okgb7jHi3He7DMv:h6N:W15Go9 ; OLa p:p6ITR + +2187/04/28 ! (eq) I87a 1T 0f95 + (wD) ZGCAmF-128472 + ; h6161Ck29nuU0N764bi 4g:0W + (Ri:q26Y9xE ubzMbbB672:pM0:QA) 68525.6QBwZl @ 750604 joWS + +2187/04/30=1987/06/03 ! (B) tv2:A:T22kI16n5J6mbHh:qZj:E:86 + (ktL4z m2B5cKS4m53CgD:MnTTq584P8:6) 655291 eOEUll @ 594372OkUXd ; upZep79v + (bYT3j0yJy8fIq:2O48R70YV5T34) -498403 RaGA @ 1.611509AfRXi + G:2XSwTH3:WI7Y8:H:Kk 134595OIIOE + ; p:38 jzL2NZhQjy5fIB0S:ixVU0 + [BTLZ Y7Kd8W:mo6rR] -142806 ZUSM @ iV629749 + ; VRFPZ40e:B8vMk8VYpyaxYT T1f1jY8qHg69LxTr + (QzQvt5:a) 863612 C + ; UW7ldN1R6:JV79EzO9I5nkQKqXNx + (K0jG1Nn6xW6qmGoK852Wm82P891w O) w -639451.0 + ; b2ERs49v6Ri734X01 + STI0MT + ; kpM7x:a6:n61Z1k7 uY4ABsJ93 + +2187/05/06=1987/06/07 ! (DWS3) G Z r2r iYLKfxR7:vrl9vr707P6pb 94XSd + HC5FR0S vxXRGK -142673 @ xAV 605535 + [Fy1:u15syG 8:8C 38vB:178ExtQk5eBMq7F:blU] -130770oz + QvJZ:t xw:N + +2187/05/09 * (nKVz6) a9:T7 + ; eT75RyW4Xd8a f1a679UnNi SH065V KnOo9 + l7liL:4Qnz07kFUQ 800903 emTA @ 976479MTcm + (a:d) -761443Ay @ 0.576885 TDa + i + +2187/05/12=1987/06/09 ! (G3CN) nm r5 + (rsiBbx8T EXY5) 386928 flTii @ 2.000225 DE + HQg3:FK1EgO -941250jElq ; Yn6gjO + C42Y z:mOpuX:f:nnk:tx + +2187/05/13 * (r6e y0) V65by ; Z X8Whns + [h96nQFF32fyO8y] 968994 UBNDg + ; T72YEmnsvcmFX91DLNMg w2948464 + [Qwk40WBS0:N2V WUW:BM7M4:09961G0] 130897QkeP @ 719156 E + l5C402g:8ym9I0T1id zl7:sa811F1q vbUv:8Q + ; t5AnGg88R45W:S161kRC0o +>>>2 +=== 0 diff --git a/test/baseline/opt-sort-all.test b/test/baseline/opt-sort-all.test new file mode 100644 index 00000000..b289f8e8 --- /dev/null +++ b/test/baseline/opt-sort-all.test @@ -0,0 +1,122 @@ +reg --monthly --sort=-amount +<<< +2008/01/11 LIAT + Expenses:Travel:Airfare $40.00 + Liabilities:MasterCard + +2008/01/14 cheaptickets.com + Expenses:Travel:Airfare $182.19 + Liabilities:MasterCard + +2008/02/05 CTX + Expenses:Travel:Auto $240.38 + Liabilities:MasterCard + +2008/02/05 UNITED + Expenses:Travel:Airfare $238.80 + Liabilities:MasterCard + +2008/02/05 UNITED + Expenses:Travel:Airfare $238.80 + Liabilities:MasterCard + +2008/02/22 BUDGET RENT-A-CAR + Expenses:Travel:Auto $40.59 + Liabilities:MasterCard + +2008/03/16 IBERIA + Expenses:Travel:Airfare $1,231.60 + Liabilities:MasterCard + +2008/03/16 IBERIA + Expenses:Travel:Airfare $1,231.60 + Liabilities:MasterCard + +2008/04/03 AMERICAN + Expenses:Travel:Airfare $155.86 + Liabilities:MasterCard + +2008/04/03 AMERICAN + Expenses:Travel:Airfare $155.86 + Liabilities:MasterCard + +2008/04/30 UNITED + Expenses:Travel:Airfare $437.21 + Liabilities:MasterCard + +2008/04/30 UNITED + Expenses:Travel:Airfare $437.21 + Liabilities:MasterCard + +2008/08/08 BCIS I-131 FILING FEE- + Expenses:Travel:Passport $170.00 + Liabilities:MasterCard + +2008/09/06 AMERICAN + Expenses:Travel:Airfare $912.60 + Liabilities:MasterCard + +2008/09/06 AMERICAN + Expenses:Travel:Airfare $912.60 + Liabilities:MasterCard + +2008/09/22 AGNT FEE + Expenses:Travel:Airfare $70.00 + Liabilities:MasterCard + +2008/09/22 DELTA + Expenses:Travel:Airfare $806.20 + Liabilities:MasterCard + +2008/09/22 DELTA + Expenses:Travel:Airfare $806.20 + Liabilities:MasterCard + +2008/09/22 LIAT 1974 LIMITED + Expenses:Travel:Airfare $418.34 + Liabilities:MasterCard + +2008/12/26 U.S. Department of State + Expenses:Travel:Passport $127.00 + Assets:Checking + +2008/12/26 U.S. Department of State + Expenses:Travel:Passport $127.00 + Assets:Checking +>>>1 +08-Jan-01 - 08-Jan-31 Ex:Travel:Airfare $222.19 $222.19 + Liabilities:MasterCard $-222.19 0 +08-Feb-01 - 08-Feb-29 Ex:Travel:Airfare $477.60 $477.60 + Expenses:Travel:Auto $280.97 $758.57 + Liabilities:MasterCard $-758.57 0 +08-Mar-01 - 08-Mar-31 Ex:Travel:Airfare $2,463.20 $2,463.20 + Liabilities:MasterCard $-2,463.20 0 +08-Apr-01 - 08-Apr-30 Ex:Travel:Airfare $1,186.14 $1,186.14 + Liabilities:MasterCard $-1,186.14 0 +08-Aug-01 - 08-Aug-31 Ex:Travel:Passport $170.00 $170.00 + Liabilities:MasterCard $-170.00 0 +08-Sep-01 - 08-Sep-30 Ex:Travel:Airfare $3,925.94 $3,925.94 + Liabilities:MasterCard $-3,925.94 0 +08-Dec-01 - 08-Dec-31 Ex:Travel:Passport $254.00 $254.00 + Assets:Checking $-254.00 0 +>>>2 +=== 0 +reg --monthly --sort-all=-amount +>>>1 +08-Sep-01 - 08-Sep-30 Ex:Travel:Airfare $3,925.94 $3,925.94 +08-Mar-01 - 08-Mar-31 Ex:Travel:Airfare $2,463.20 $6,389.14 +08-Apr-01 - 08-Apr-30 Ex:Travel:Airfare $1,186.14 $7,575.28 +08-Feb-01 - 08-Feb-29 Ex:Travel:Airfare $477.60 $8,052.88 + Expenses:Travel:Auto $280.97 $8,333.85 +08-Dec-01 - 08-Dec-31 Ex:Travel:Passport $254.00 $8,587.85 +08-Jan-01 - 08-Jan-31 Ex:Travel:Airfare $222.19 $8,810.04 +08-Aug-01 - 08-Aug-31 Ex:Travel:Passport $170.00 $8,980.04 + Liabilities:MasterCard $-170.00 $8,810.04 +08-Jan-01 - 08-Jan-31 Liabilities:MasterCard $-222.19 $8,587.85 +08-Dec-01 - 08-Dec-31 Assets:Checking $-254.00 $8,333.85 +08-Feb-01 - 08-Feb-29 Liabilities:MasterCard $-758.57 $7,575.28 +08-Apr-01 - 08-Apr-30 Liabilities:MasterCard $-1,186.14 $6,389.14 +08-Mar-01 - 08-Mar-31 Liabilities:MasterCard $-2,463.20 $3,925.94 +08-Sep-01 - 08-Sep-30 Liabilities:MasterCard $-3,925.94 0 +>>>2 +=== 0 diff --git a/test/baseline/opt-sort-xacts.test b/test/baseline/opt-sort-xacts.test new file mode 100644 index 00000000..4882e18f --- /dev/null +++ b/test/baseline/opt-sort-xacts.test @@ -0,0 +1,176 @@ +reg --sort=account +<<< +2008/01/11 LIAT + Expenses:Travel:Airfare $40.00 + Liabilities:MasterCard + +2008/01/14 cheaptickets.com + Expenses:Travel:Airfare $182.19 + Liabilities:MasterCard + +2008/02/05 CTX + Expenses:Travel:Auto $240.38 + Liabilities:MasterCard + +2008/02/05 UNITED + Expenses:Travel:Airfare $238.80 + Liabilities:MasterCard + +2008/02/05 UNITED + Expenses:Travel:Airfare $238.80 + Liabilities:MasterCard + +2008/02/22 BUDGET RENT-A-CAR + Expenses:Travel:Auto $40.59 + Liabilities:MasterCard + +2008/03/16 IBERIA + Expenses:Travel:Airfare $1,231.60 + Liabilities:MasterCard + +2008/03/16 IBERIA + Expenses:Travel:Airfare $1,231.60 + Liabilities:MasterCard + +2008/04/03 AMERICAN + Expenses:Travel:Airfare $155.86 + Liabilities:MasterCard + +2008/04/03 AMERICAN + Expenses:Travel:Airfare $155.86 + Liabilities:MasterCard + +2008/04/30 UNITED + Expenses:Travel:Airfare $437.21 + Liabilities:MasterCard + +2008/04/30 UNITED + Expenses:Travel:Airfare $437.21 + Liabilities:MasterCard + +2008/08/08 BCIS I-131 FILING FEE- + Expenses:Travel:Passport $170.00 + Liabilities:MasterCard + +2008/09/06 AMERICAN + Expenses:Travel:Airfare $912.60 + Liabilities:MasterCard + +2008/09/06 AMERICAN + Expenses:Travel:Airfare $912.60 + Liabilities:MasterCard + +2008/09/22 AGNT FEE + Expenses:Travel:Airfare $70.00 + Liabilities:MasterCard + +2008/09/22 DELTA + Expenses:Travel:Airfare $806.20 + Liabilities:MasterCard + +2008/09/22 DELTA + Expenses:Travel:Airfare $806.20 + Liabilities:MasterCard + +2008/09/22 LIAT 1974 LIMITED + Expenses:Travel:Airfare $418.34 + Liabilities:MasterCard + +2008/12/26 U.S. Department of State + Expenses:Travel:Passport $127.00 + Assets:Checking + +2008/12/26 U.S. Department of State + Expenses:Travel:Passport $127.00 + Assets:Checking +>>>1 +08-Dec-26 U.S. Department of .. Assets:Checking $-127.00 $-127.00 +08-Dec-26 U.S. Department of .. Assets:Checking $-127.00 $-254.00 +08-Jan-11 LIAT Ex:Travel:Airfare $40.00 $-214.00 +08-Jan-14 cheaptickets.com Ex:Travel:Airfare $182.19 $-31.81 +08-Feb-05 UNITED Ex:Travel:Airfare $238.80 $206.99 +08-Feb-05 UNITED Ex:Travel:Airfare $238.80 $445.79 +08-Mar-16 IBERIA Ex:Travel:Airfare $1,231.60 $1,677.39 +08-Mar-16 IBERIA Ex:Travel:Airfare $1,231.60 $2,908.99 +08-Apr-03 AMERICAN Ex:Travel:Airfare $155.86 $3,064.85 +08-Apr-03 AMERICAN Ex:Travel:Airfare $155.86 $3,220.71 +08-Apr-30 UNITED Ex:Travel:Airfare $437.21 $3,657.92 +08-Apr-30 UNITED Ex:Travel:Airfare $437.21 $4,095.13 +08-Sep-06 AMERICAN Ex:Travel:Airfare $912.60 $5,007.73 +08-Sep-06 AMERICAN Ex:Travel:Airfare $912.60 $5,920.33 +08-Sep-22 AGNT FEE Ex:Travel:Airfare $70.00 $5,990.33 +08-Sep-22 DELTA Ex:Travel:Airfare $806.20 $6,796.53 +08-Sep-22 DELTA Ex:Travel:Airfare $806.20 $7,602.73 +08-Sep-22 LIAT 1974 LIMITED Ex:Travel:Airfare $418.34 $8,021.07 +08-Feb-05 CTX Expenses:Travel:Auto $240.38 $8,261.45 +08-Feb-22 BUDGET RENT-A-CAR Expenses:Travel:Auto $40.59 $8,302.04 +08-Aug-08 BCIS I-131 FILING F.. Ex:Travel:Passport $170.00 $8,472.04 +08-Dec-26 U.S. Department of .. Ex:Travel:Passport $127.00 $8,599.04 +08-Dec-26 U.S. Department of .. Ex:Travel:Passport $127.00 $8,726.04 +08-Jan-11 LIAT Liabilities:MasterCard $-40.00 $8,686.04 +08-Jan-14 cheaptickets.com Liabilities:MasterCard $-182.19 $8,503.85 +08-Feb-05 CTX Liabilities:MasterCard $-240.38 $8,263.47 +08-Feb-05 UNITED Liabilities:MasterCard $-238.80 $8,024.67 +08-Feb-05 UNITED Liabilities:MasterCard $-238.80 $7,785.87 +08-Feb-22 BUDGET RENT-A-CAR Liabilities:MasterCard $-40.59 $7,745.28 +08-Mar-16 IBERIA Liabilities:MasterCard $-1,231.60 $6,513.68 +08-Mar-16 IBERIA Liabilities:MasterCard $-1,231.60 $5,282.08 +08-Apr-03 AMERICAN Liabilities:MasterCard $-155.86 $5,126.22 +08-Apr-03 AMERICAN Liabilities:MasterCard $-155.86 $4,970.36 +08-Apr-30 UNITED Liabilities:MasterCard $-437.21 $4,533.15 +08-Apr-30 UNITED Liabilities:MasterCard $-437.21 $4,095.94 +08-Aug-08 BCIS I-131 FILING F.. Liabilities:MasterCard $-170.00 $3,925.94 +08-Sep-06 AMERICAN Liabilities:MasterCard $-912.60 $3,013.34 +08-Sep-06 AMERICAN Liabilities:MasterCard $-912.60 $2,100.74 +08-Sep-22 AGNT FEE Liabilities:MasterCard $-70.00 $2,030.74 +08-Sep-22 DELTA Liabilities:MasterCard $-806.20 $1,224.54 +08-Sep-22 DELTA Liabilities:MasterCard $-806.20 $418.34 +08-Sep-22 LIAT 1974 LIMITED Liabilities:MasterCard $-418.34 0 +>>>2 +=== 0 +reg --sort-xacts=account +>>>1 +08-Jan-11 LIAT Ex:Travel:Airfare $40.00 $40.00 + Liabilities:MasterCard $-40.00 0 +08-Jan-14 cheaptickets.com Ex:Travel:Airfare $182.19 $182.19 + Liabilities:MasterCard $-182.19 0 +08-Feb-05 CTX Expenses:Travel:Auto $240.38 $240.38 + Liabilities:MasterCard $-240.38 0 +08-Feb-05 UNITED Ex:Travel:Airfare $238.80 $238.80 + Liabilities:MasterCard $-238.80 0 +08-Feb-05 UNITED Ex:Travel:Airfare $238.80 $238.80 + Liabilities:MasterCard $-238.80 0 +08-Feb-22 BUDGET RENT-A-CAR Expenses:Travel:Auto $40.59 $40.59 + Liabilities:MasterCard $-40.59 0 +08-Mar-16 IBERIA Ex:Travel:Airfare $1,231.60 $1,231.60 + Liabilities:MasterCard $-1,231.60 0 +08-Mar-16 IBERIA Ex:Travel:Airfare $1,231.60 $1,231.60 + Liabilities:MasterCard $-1,231.60 0 +08-Apr-03 AMERICAN Ex:Travel:Airfare $155.86 $155.86 + Liabilities:MasterCard $-155.86 0 +08-Apr-03 AMERICAN Ex:Travel:Airfare $155.86 $155.86 + Liabilities:MasterCard $-155.86 0 +08-Apr-30 UNITED Ex:Travel:Airfare $437.21 $437.21 + Liabilities:MasterCard $-437.21 0 +08-Apr-30 UNITED Ex:Travel:Airfare $437.21 $437.21 + Liabilities:MasterCard $-437.21 0 +08-Aug-08 BCIS I-131 FILING F.. Ex:Travel:Passport $170.00 $170.00 + Liabilities:MasterCard $-170.00 0 +08-Sep-06 AMERICAN Ex:Travel:Airfare $912.60 $912.60 + Liabilities:MasterCard $-912.60 0 +08-Sep-06 AMERICAN Ex:Travel:Airfare $912.60 $912.60 + Liabilities:MasterCard $-912.60 0 +08-Sep-22 AGNT FEE Ex:Travel:Airfare $70.00 $70.00 + Liabilities:MasterCard $-70.00 0 +08-Sep-22 DELTA Ex:Travel:Airfare $806.20 $806.20 + Liabilities:MasterCard $-806.20 0 +08-Sep-22 DELTA Ex:Travel:Airfare $806.20 $806.20 + Liabilities:MasterCard $-806.20 0 +08-Sep-22 LIAT 1974 LIMITED Ex:Travel:Airfare $418.34 $418.34 + Liabilities:MasterCard $-418.34 0 +08-Dec-26 U.S. Department of .. Assets:Checking $-127.00 $-127.00 + Ex:Travel:Passport $127.00 0 +08-Dec-26 U.S. Department of .. Assets:Checking $-127.00 $-127.00 + Ex:Travel:Passport $127.00 0 +>>>2 +=== 0 diff --git a/test/baseline/opt-sort.test b/test/baseline/opt-sort.test new file mode 100644 index 00000000..27efe31b --- /dev/null +++ b/test/baseline/opt-sort.test @@ -0,0 +1,246 @@ +reg airfare --sort=date +<<< +2008/01/11 LIAT + Expenses:Travel:Airfare $40.00 + Liabilities:MasterCard + +2008/01/14 cheaptickets.com + Expenses:Travel:Airfare $182.19 + Liabilities:MasterCard + +2008/02/05 CTX + Expenses:Travel:Auto $240.38 + Liabilities:MasterCard + +2008/02/05 UNITED + Expenses:Travel:Airfare $238.80 + Liabilities:MasterCard + +2008/02/05 UNITED + Expenses:Travel:Airfare $238.80 + Liabilities:MasterCard + +2008/02/22 BUDGET RENT-A-CAR + Expenses:Travel:Auto $40.59 + Liabilities:MasterCard + +2008/03/16 IBERIA + Expenses:Travel:Airfare $1,231.60 + Liabilities:MasterCard + +2008/03/16 IBERIA + Expenses:Travel:Airfare $1,231.60 + Liabilities:MasterCard + +2008/04/03 AMERICAN + Expenses:Travel:Airfare $155.86 + Liabilities:MasterCard + +2008/04/03 AMERICAN + Expenses:Travel:Airfare $155.86 + Liabilities:MasterCard + +2008/04/30 UNITED + Expenses:Travel:Airfare $437.21 + Liabilities:MasterCard + +2008/04/30 UNITED + Expenses:Travel:Airfare $437.21 + Liabilities:MasterCard + +2008/08/08 BCIS I-131 FILING FEE- + Expenses:Travel:Passport $170.00 + Liabilities:MasterCard + +2008/09/06 AMERICAN + Expenses:Travel:Airfare $912.60 + Liabilities:MasterCard + +2008/09/06 AMERICAN + Expenses:Travel:Airfare $912.60 + Liabilities:MasterCard + +2008/09/22 AGNT FEE + Expenses:Travel:Airfare $70.00 + Liabilities:MasterCard + +2008/09/22 DELTA + Expenses:Travel:Airfare $806.20 + Liabilities:MasterCard + +2008/09/22 DELTA + Expenses:Travel:Airfare $806.20 + Liabilities:MasterCard + +2008/09/22 LIAT 1974 LIMITED + Expenses:Travel:Airfare $418.34 + Liabilities:MasterCard + +2008/12/26 U.S. Department of State + Expenses:Travel:Passport $127.00 + Assets:Checking + +2008/12/26 U.S. Department of State + Expenses:Travel:Passport $127.00 + Assets:Checking +>>>1 +08-Jan-11 LIAT Ex:Travel:Airfare $40.00 $40.00 +08-Jan-14 cheaptickets.com Ex:Travel:Airfare $182.19 $222.19 +08-Feb-05 UNITED Ex:Travel:Airfare $238.80 $460.99 +08-Feb-05 UNITED Ex:Travel:Airfare $238.80 $699.79 +08-Mar-16 IBERIA Ex:Travel:Airfare $1,231.60 $1,931.39 +08-Mar-16 IBERIA Ex:Travel:Airfare $1,231.60 $3,162.99 +08-Apr-03 AMERICAN Ex:Travel:Airfare $155.86 $3,318.85 +08-Apr-03 AMERICAN Ex:Travel:Airfare $155.86 $3,474.71 +08-Apr-30 UNITED Ex:Travel:Airfare $437.21 $3,911.92 +08-Apr-30 UNITED Ex:Travel:Airfare $437.21 $4,349.13 +08-Sep-06 AMERICAN Ex:Travel:Airfare $912.60 $5,261.73 +08-Sep-06 AMERICAN Ex:Travel:Airfare $912.60 $6,174.33 +08-Sep-22 AGNT FEE Ex:Travel:Airfare $70.00 $6,244.33 +08-Sep-22 DELTA Ex:Travel:Airfare $806.20 $7,050.53 +08-Sep-22 DELTA Ex:Travel:Airfare $806.20 $7,856.73 +08-Sep-22 LIAT 1974 LIMITED Ex:Travel:Airfare $418.34 $8,275.07 +>>>2 +=== 0 +reg airfare --sort=date,amount +>>>1 +08-Jan-11 LIAT Ex:Travel:Airfare $40.00 $40.00 +08-Jan-14 cheaptickets.com Ex:Travel:Airfare $182.19 $222.19 +08-Feb-05 UNITED Ex:Travel:Airfare $238.80 $460.99 +08-Feb-05 UNITED Ex:Travel:Airfare $238.80 $699.79 +08-Mar-16 IBERIA Ex:Travel:Airfare $1,231.60 $1,931.39 +08-Mar-16 IBERIA Ex:Travel:Airfare $1,231.60 $3,162.99 +08-Apr-03 AMERICAN Ex:Travel:Airfare $155.86 $3,318.85 +08-Apr-03 AMERICAN Ex:Travel:Airfare $155.86 $3,474.71 +08-Apr-30 UNITED Ex:Travel:Airfare $437.21 $3,911.92 +08-Apr-30 UNITED Ex:Travel:Airfare $437.21 $4,349.13 +08-Sep-06 AMERICAN Ex:Travel:Airfare $912.60 $5,261.73 +08-Sep-06 AMERICAN Ex:Travel:Airfare $912.60 $6,174.33 +08-Sep-22 AGNT FEE Ex:Travel:Airfare $70.00 $6,244.33 +08-Sep-22 LIAT 1974 LIMITED Ex:Travel:Airfare $418.34 $6,662.67 +08-Sep-22 DELTA Ex:Travel:Airfare $806.20 $7,468.87 +08-Sep-22 DELTA Ex:Travel:Airfare $806.20 $8,275.07 +>>>2 +=== 0 +reg airfare --sort=date,-amount +>>>1 +08-Jan-11 LIAT Ex:Travel:Airfare $40.00 $40.00 +08-Jan-14 cheaptickets.com Ex:Travel:Airfare $182.19 $222.19 +08-Feb-05 UNITED Ex:Travel:Airfare $238.80 $460.99 +08-Feb-05 UNITED Ex:Travel:Airfare $238.80 $699.79 +08-Mar-16 IBERIA Ex:Travel:Airfare $1,231.60 $1,931.39 +08-Mar-16 IBERIA Ex:Travel:Airfare $1,231.60 $3,162.99 +08-Apr-03 AMERICAN Ex:Travel:Airfare $155.86 $3,318.85 +08-Apr-03 AMERICAN Ex:Travel:Airfare $155.86 $3,474.71 +08-Apr-30 UNITED Ex:Travel:Airfare $437.21 $3,911.92 +08-Apr-30 UNITED Ex:Travel:Airfare $437.21 $4,349.13 +08-Sep-06 AMERICAN Ex:Travel:Airfare $912.60 $5,261.73 +08-Sep-06 AMERICAN Ex:Travel:Airfare $912.60 $6,174.33 +08-Sep-22 DELTA Ex:Travel:Airfare $806.20 $6,980.53 +08-Sep-22 DELTA Ex:Travel:Airfare $806.20 $7,786.73 +08-Sep-22 LIAT 1974 LIMITED Ex:Travel:Airfare $418.34 $8,205.07 +08-Sep-22 AGNT FEE Ex:Travel:Airfare $70.00 $8,275.07 +>>>2 +=== 0 +reg airfare --sort=-date,-amount +>>>1 +08-Sep-22 DELTA Ex:Travel:Airfare $806.20 $806.20 +08-Sep-22 DELTA Ex:Travel:Airfare $806.20 $1,612.40 +08-Sep-22 LIAT 1974 LIMITED Ex:Travel:Airfare $418.34 $2,030.74 +08-Sep-22 AGNT FEE Ex:Travel:Airfare $70.00 $2,100.74 +08-Sep-06 AMERICAN Ex:Travel:Airfare $912.60 $3,013.34 +08-Sep-06 AMERICAN Ex:Travel:Airfare $912.60 $3,925.94 +08-Apr-30 UNITED Ex:Travel:Airfare $437.21 $4,363.15 +08-Apr-30 UNITED Ex:Travel:Airfare $437.21 $4,800.36 +08-Apr-03 AMERICAN Ex:Travel:Airfare $155.86 $4,956.22 +08-Apr-03 AMERICAN Ex:Travel:Airfare $155.86 $5,112.08 +08-Mar-16 IBERIA Ex:Travel:Airfare $1,231.60 $6,343.68 +08-Mar-16 IBERIA Ex:Travel:Airfare $1,231.60 $7,575.28 +08-Feb-05 UNITED Ex:Travel:Airfare $238.80 $7,814.08 +08-Feb-05 UNITED Ex:Travel:Airfare $238.80 $8,052.88 +08-Jan-14 cheaptickets.com Ex:Travel:Airfare $182.19 $8,235.07 +08-Jan-11 LIAT Ex:Travel:Airfare $40.00 $8,275.07 +>>>2 +=== 0 +bal --sort=total +>>>1 + $-8,726.04 Liabilities:MasterCard + $-254.00 Assets:Checking + $8,980.04 Expenses:Travel + $280.97 Auto + $424.00 Passport + $8,275.07 Airfare +-------------------- + 0 +>>>2 +=== 0 +bal --sort=-total +>>>1 + $8,980.04 Expenses:Travel + $8,275.07 Airfare + $424.00 Passport + $280.97 Auto + $-254.00 Assets:Checking + $-8,726.04 Liabilities:MasterCard +-------------------- + 0 +>>>2 +=== 0 +bal --sort=-account +>>>1 + $-8,726.04 Liabilities:MasterCard + $8,980.04 Expenses:Travel + $424.00 Passport + $280.97 Auto + $8,275.07 Airfare + $-254.00 Assets:Checking +-------------------- + 0 +>>>2 +=== 0 +reg --sort=account +>>>1 +08-Dec-26 U.S. Department of .. Assets:Checking $-127.00 $-127.00 +08-Dec-26 U.S. Department of .. Assets:Checking $-127.00 $-254.00 +08-Jan-11 LIAT Ex:Travel:Airfare $40.00 $-214.00 +08-Jan-14 cheaptickets.com Ex:Travel:Airfare $182.19 $-31.81 +08-Feb-05 UNITED Ex:Travel:Airfare $238.80 $206.99 +08-Feb-05 UNITED Ex:Travel:Airfare $238.80 $445.79 +08-Mar-16 IBERIA Ex:Travel:Airfare $1,231.60 $1,677.39 +08-Mar-16 IBERIA Ex:Travel:Airfare $1,231.60 $2,908.99 +08-Apr-03 AMERICAN Ex:Travel:Airfare $155.86 $3,064.85 +08-Apr-03 AMERICAN Ex:Travel:Airfare $155.86 $3,220.71 +08-Apr-30 UNITED Ex:Travel:Airfare $437.21 $3,657.92 +08-Apr-30 UNITED Ex:Travel:Airfare $437.21 $4,095.13 +08-Sep-06 AMERICAN Ex:Travel:Airfare $912.60 $5,007.73 +08-Sep-06 AMERICAN Ex:Travel:Airfare $912.60 $5,920.33 +08-Sep-22 AGNT FEE Ex:Travel:Airfare $70.00 $5,990.33 +08-Sep-22 DELTA Ex:Travel:Airfare $806.20 $6,796.53 +08-Sep-22 DELTA Ex:Travel:Airfare $806.20 $7,602.73 +08-Sep-22 LIAT 1974 LIMITED Ex:Travel:Airfare $418.34 $8,021.07 +08-Feb-05 CTX Expenses:Travel:Auto $240.38 $8,261.45 +08-Feb-22 BUDGET RENT-A-CAR Expenses:Travel:Auto $40.59 $8,302.04 +08-Aug-08 BCIS I-131 FILING F.. Ex:Travel:Passport $170.00 $8,472.04 +08-Dec-26 U.S. Department of .. Ex:Travel:Passport $127.00 $8,599.04 +08-Dec-26 U.S. Department of .. Ex:Travel:Passport $127.00 $8,726.04 +08-Jan-11 LIAT Liabilities:MasterCard $-40.00 $8,686.04 +08-Jan-14 cheaptickets.com Liabilities:MasterCard $-182.19 $8,503.85 +08-Feb-05 CTX Liabilities:MasterCard $-240.38 $8,263.47 +08-Feb-05 UNITED Liabilities:MasterCard $-238.80 $8,024.67 +08-Feb-05 UNITED Liabilities:MasterCard $-238.80 $7,785.87 +08-Feb-22 BUDGET RENT-A-CAR Liabilities:MasterCard $-40.59 $7,745.28 +08-Mar-16 IBERIA Liabilities:MasterCard $-1,231.60 $6,513.68 +08-Mar-16 IBERIA Liabilities:MasterCard $-1,231.60 $5,282.08 +08-Apr-03 AMERICAN Liabilities:MasterCard $-155.86 $5,126.22 +08-Apr-03 AMERICAN Liabilities:MasterCard $-155.86 $4,970.36 +08-Apr-30 UNITED Liabilities:MasterCard $-437.21 $4,533.15 +08-Apr-30 UNITED Liabilities:MasterCard $-437.21 $4,095.94 +08-Aug-08 BCIS I-131 FILING F.. Liabilities:MasterCard $-170.00 $3,925.94 +08-Sep-06 AMERICAN Liabilities:MasterCard $-912.60 $3,013.34 +08-Sep-06 AMERICAN Liabilities:MasterCard $-912.60 $2,100.74 +08-Sep-22 AGNT FEE Liabilities:MasterCard $-70.00 $2,030.74 +08-Sep-22 DELTA Liabilities:MasterCard $-806.20 $1,224.54 +08-Sep-22 DELTA Liabilities:MasterCard $-806.20 $418.34 +08-Sep-22 LIAT 1974 LIMITED Liabilities:MasterCard $-418.34 0 +>>>2 +=== 0 diff --git a/test/baseline/opt-subtotal.test b/test/baseline/opt-subtotal.test new file mode 100644 index 00000000..41defbc1 --- /dev/null +++ b/test/baseline/opt-subtotal.test @@ -0,0 +1,93 @@ +reg --subtotal +<<< +2008/01/11 LIAT + Expenses:Travel:Airfare $40.00 + Liabilities:MasterCard + +2008/01/14 cheaptickets.com + Expenses:Travel:Airfare $182.19 + Liabilities:MasterCard + +2008/02/05 CTX + Expenses:Travel:Auto $240.38 + Liabilities:MasterCard + +2008/02/05 UNITED + Expenses:Travel:Airfare $238.80 + Liabilities:MasterCard + +2008/02/05 UNITED + Expenses:Travel:Airfare $238.80 + Liabilities:MasterCard + +2008/02/22 BUDGET RENT-A-CAR + Expenses:Travel:Auto $40.59 + Liabilities:MasterCard + +2008/03/16 IBERIA + Expenses:Travel:Airfare $1,231.60 + Liabilities:MasterCard + +2008/03/16 IBERIA + Expenses:Travel:Airfare $1,231.60 + Liabilities:MasterCard + +2008/04/03 AMERICAN + Expenses:Travel:Airfare $155.86 + Liabilities:MasterCard + +2008/04/03 AMERICAN + Expenses:Travel:Airfare $155.86 + Liabilities:MasterCard + +2008/04/30 UNITED + Expenses:Travel:Airfare $437.21 + Liabilities:MasterCard + +2008/04/30 UNITED + Expenses:Travel:Airfare $437.21 + Liabilities:MasterCard + +2008/08/08 BCIS I-131 FILING FEE- + Expenses:Travel:Passport $170.00 + Liabilities:MasterCard + +2008/09/06 AMERICAN + Expenses:Travel:Airfare $912.60 + Liabilities:MasterCard + +2008/09/06 AMERICAN + Expenses:Travel:Airfare $912.60 + Liabilities:MasterCard + +2008/09/22 AGNT FEE + Expenses:Travel:Airfare $70.00 + Liabilities:MasterCard + +2008/09/22 DELTA + Expenses:Travel:Airfare $806.20 + Liabilities:MasterCard + +2008/09/22 DELTA + Expenses:Travel:Airfare $806.20 + Liabilities:MasterCard + +2008/09/22 LIAT 1974 LIMITED + Expenses:Travel:Airfare $418.34 + Liabilities:MasterCard + +2008/12/26 U.S. Department of State + Expenses:Travel:Passport $127.00 + Assets:Checking + +2008/12/26 U.S. Department of State + Expenses:Travel:Passport $127.00 + Assets:Checking +>>>1 +08-Jan-11 - 08-Dec-26 Assets:Checking $-254.00 $-254.00 + Ex:Travel:Airfare $8,275.07 $8,021.07 + Expenses:Travel:Auto $280.97 $8,302.04 + Ex:Travel:Passport $424.00 $8,726.04 + Liabilities:MasterCard $-8,726.04 0 +>>>2 +=== 0 diff --git a/test/baseline/opt-total-data.test b/test/baseline/opt-total-data.test new file mode 100644 index 00000000..f1ab33c1 --- /dev/null +++ b/test/baseline/opt-total-data.test @@ -0,0 +1,10 @@ +reg --market --total-data +<<< +2007/02/02 RD VMMXX + Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 + Income:Dividends:Vanguard:VMMXX $-0.35 +>>>1 +2007-02-02 0.35 +2007-02-02 0 +>>>2 +=== 0 diff --git a/test/baseline/opt-total.test b/test/baseline/opt-total.test new file mode 100644 index 00000000..8f4719d2 --- /dev/null +++ b/test/baseline/opt-total.test @@ -0,0 +1,10 @@ +reg --total=10 +<<< +2007/02/02 RD VMMXX + Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 + Income:Dividends:Vanguard:VMMXX $-0.35 +>>>1 +07-Feb-02 RD VMMXX As:In:Vanguard:VMMXX 0.350 VMMXX 10 + In:Di:Vanguard:VMMXX $-0.35 10 +>>>2 +=== 0 diff --git a/test/baseline/opt-truncate.test b/test/baseline/opt-truncate.test new file mode 100644 index 00000000..0f9190c2 --- /dev/null +++ b/test/baseline/opt-truncate.test @@ -0,0 +1,144 @@ +reg airfare --truncate=leading +<<< +2008/01/11 LIAT + Expenses:Travel:Airfare $40.00 + Liabilities:MasterCard + +2008/01/14 cheaptickets.com + Expenses:Travel:Airfare $182.19 + Liabilities:MasterCard + +2008/02/05 CTX + Expenses:Travel:Auto $240.38 + Liabilities:MasterCard + +2008/02/05 UNITED + Expenses:Travel:Airfare $238.80 + Liabilities:MasterCard + +2008/02/05 UNITED + Expenses:Travel:Airfare $238.80 + Liabilities:MasterCard + +2008/02/22 BUDGET RENT-A-CAR + Expenses:Travel:Auto $40.59 + Liabilities:MasterCard + +2008/03/16 IBERIA + Expenses:Travel:Airfare $1,231.60 + Liabilities:MasterCard + +2008/03/16 IBERIA + Expenses:Travel:Airfare $1,231.60 + Liabilities:MasterCard + +2008/04/03 AMERICAN + Expenses:Travel:Airfare $155.86 + Liabilities:MasterCard + +2008/04/03 AMERICAN + Expenses:Travel:Airfare $155.86 + Liabilities:MasterCard + +2008/04/30 UNITED + Expenses:Travel:Airfare $437.21 + Liabilities:MasterCard + +2008/04/30 UNITED + Expenses:Travel:Airfare $437.21 + Liabilities:MasterCard + +2008/08/08 BCIS I-131 FILING FEE- + Expenses:Travel:Passport $170.00 + Liabilities:MasterCard + +2008/09/06 AMERICAN + Expenses:Travel:Airfare $912.60 + Liabilities:MasterCard + +2008/09/06 AMERICAN + Expenses:Travel:Airfare $912.60 + Liabilities:MasterCard + +2008/09/22 AGNT FEE + Expenses:Travel:Airfare $70.00 + Liabilities:MasterCard + +2008/09/22 DELTA + Expenses:Travel:Airfare $806.20 + Liabilities:MasterCard + +2008/09/22 DELTA + Expenses:Travel:Airfare $806.20 + Liabilities:MasterCard + +2008/09/22 LIAT 1974 LIMITED + Expenses:Travel:Airfare $418.34 + Liabilities:MasterCard + +2008/12/26 U.S. Department of State + Expenses:Travel:Passport $127.00 + Assets:Checking + +2008/12/26 U.S. Department of State + Expenses:Travel:Passport $127.00 + Assets:Checking +>>>1 +08-Jan-11 LIAT ..enses:Travel:Airfare $40.00 $40.00 +08-Jan-14 cheaptickets.com ..enses:Travel:Airfare $182.19 $222.19 +08-Feb-05 UNITED ..enses:Travel:Airfare $238.80 $460.99 +08-Feb-05 UNITED ..enses:Travel:Airfare $238.80 $699.79 +08-Mar-16 IBERIA ..enses:Travel:Airfare $1,231.60 $1,931.39 +08-Mar-16 IBERIA ..enses:Travel:Airfare $1,231.60 $3,162.99 +08-Apr-03 AMERICAN ..enses:Travel:Airfare $155.86 $3,318.85 +08-Apr-03 AMERICAN ..enses:Travel:Airfare $155.86 $3,474.71 +08-Apr-30 UNITED ..enses:Travel:Airfare $437.21 $3,911.92 +08-Apr-30 UNITED ..enses:Travel:Airfare $437.21 $4,349.13 +08-Sep-06 AMERICAN ..enses:Travel:Airfare $912.60 $5,261.73 +08-Sep-06 AMERICAN ..enses:Travel:Airfare $912.60 $6,174.33 +08-Sep-22 AGNT FEE ..enses:Travel:Airfare $70.00 $6,244.33 +08-Sep-22 DELTA ..enses:Travel:Airfare $806.20 $7,050.53 +08-Sep-22 DELTA ..enses:Travel:Airfare $806.20 $7,856.73 +08-Sep-22 LIAT 1974 LIMITED ..enses:Travel:Airfare $418.34 $8,275.07 +>>>2 +=== 0 +reg airfare --truncate=middle +>>>1 +08-Jan-11 LIAT Expenses:T..el:Airfare $40.00 $40.00 +08-Jan-14 cheaptickets.com Expenses:T..el:Airfare $182.19 $222.19 +08-Feb-05 UNITED Expenses:T..el:Airfare $238.80 $460.99 +08-Feb-05 UNITED Expenses:T..el:Airfare $238.80 $699.79 +08-Mar-16 IBERIA Expenses:T..el:Airfare $1,231.60 $1,931.39 +08-Mar-16 IBERIA Expenses:T..el:Airfare $1,231.60 $3,162.99 +08-Apr-03 AMERICAN Expenses:T..el:Airfare $155.86 $3,318.85 +08-Apr-03 AMERICAN Expenses:T..el:Airfare $155.86 $3,474.71 +08-Apr-30 UNITED Expenses:T..el:Airfare $437.21 $3,911.92 +08-Apr-30 UNITED Expenses:T..el:Airfare $437.21 $4,349.13 +08-Sep-06 AMERICAN Expenses:T..el:Airfare $912.60 $5,261.73 +08-Sep-06 AMERICAN Expenses:T..el:Airfare $912.60 $6,174.33 +08-Sep-22 AGNT FEE Expenses:T..el:Airfare $70.00 $6,244.33 +08-Sep-22 DELTA Expenses:T..el:Airfare $806.20 $7,050.53 +08-Sep-22 DELTA Expenses:T..el:Airfare $806.20 $7,856.73 +08-Sep-22 LIAT 1974 LIMITED Expenses:T..el:Airfare $418.34 $8,275.07 +>>>2 +=== 0 +reg airfare --truncate=trailing +>>>1 +08-Jan-11 LIAT Expenses:Travel:Airf.. $40.00 $40.00 +08-Jan-14 cheaptickets.com Expenses:Travel:Airf.. $182.19 $222.19 +08-Feb-05 UNITED Expenses:Travel:Airf.. $238.80 $460.99 +08-Feb-05 UNITED Expenses:Travel:Airf.. $238.80 $699.79 +08-Mar-16 IBERIA Expenses:Travel:Airf.. $1,231.60 $1,931.39 +08-Mar-16 IBERIA Expenses:Travel:Airf.. $1,231.60 $3,162.99 +08-Apr-03 AMERICAN Expenses:Travel:Airf.. $155.86 $3,318.85 +08-Apr-03 AMERICAN Expenses:Travel:Airf.. $155.86 $3,474.71 +08-Apr-30 UNITED Expenses:Travel:Airf.. $437.21 $3,911.92 +08-Apr-30 UNITED Expenses:Travel:Airf.. $437.21 $4,349.13 +08-Sep-06 AMERICAN Expenses:Travel:Airf.. $912.60 $5,261.73 +08-Sep-06 AMERICAN Expenses:Travel:Airf.. $912.60 $6,174.33 +08-Sep-22 AGNT FEE Expenses:Travel:Airf.. $70.00 $6,244.33 +08-Sep-22 DELTA Expenses:Travel:Airf.. $806.20 $7,050.53 +08-Sep-22 DELTA Expenses:Travel:Airf.. $806.20 $7,856.73 +08-Sep-22 LIAT 1974 LIMITED Expenses:Travel:Airf.. $418.34 $8,275.07 +>>>2 +=== 0 diff --git a/test/baseline/opt-unround.test b/test/baseline/opt-unround.test new file mode 100644 index 00000000..70018f12 --- /dev/null +++ b/test/baseline/opt-unround.test @@ -0,0 +1,94 @@ +bal --unround --percent +<<< +2008/01/11 LIAT + Expenses:Travel:Airfare $40.00 + Liabilities:MasterCard + +2008/01/14 cheaptickets.com + Expenses:Travel:Airfare $182.19 + Liabilities:MasterCard + +2008/02/05 CTX + Expenses:Travel:Auto $240.38 + Liabilities:MasterCard + +2008/02/05 UNITED + Expenses:Travel:Airfare $238.80 + Liabilities:MasterCard + +2008/02/05 UNITED + Expenses:Travel:Airfare $238.80 + Liabilities:MasterCard + +2008/02/22 BUDGET RENT-A-CAR + Expenses:Travel:Auto $40.59 + Liabilities:MasterCard + +2008/03/16 IBERIA + Expenses:Travel:Airfare $1,231.60 + Liabilities:MasterCard + +2008/03/16 IBERIA + Expenses:Travel:Airfare $1,231.60 + Liabilities:MasterCard + +2008/04/03 AMERICAN + Expenses:Travel:Airfare $155.86 + Liabilities:MasterCard + +2008/04/03 AMERICAN + Expenses:Travel:Airfare $155.86 + Liabilities:MasterCard + +2008/04/30 UNITED + Expenses:Travel:Airfare $437.21 + Liabilities:MasterCard + +2008/04/30 UNITED + Expenses:Travel:Airfare $437.21 + Liabilities:MasterCard + +2008/08/08 BCIS I-131 FILING FEE- + Expenses:Travel:Passport $170.00 + Liabilities:MasterCard + +2008/09/06 AMERICAN + Expenses:Travel:Airfare $912.60 + Liabilities:MasterCard + +2008/09/06 AMERICAN + Expenses:Travel:Airfare $912.60 + Liabilities:MasterCard + +2008/09/22 AGNT FEE + Expenses:Travel:Airfare $70.00 + Liabilities:MasterCard + +2008/09/22 DELTA + Expenses:Travel:Airfare $806.20 + Liabilities:MasterCard + +2008/09/22 DELTA + Expenses:Travel:Airfare $806.20 + Liabilities:MasterCard + +2008/09/22 LIAT 1974 LIMITED + Expenses:Travel:Airfare $418.34 + Liabilities:MasterCard + +2008/12/26 U.S. Department of State + Expenses:Travel:Passport $127.00 + Assets:Checking + +2008/12/26 U.S. Department of State + Expenses:Travel:Passport $127.00 + Assets:Checking +>>>1 + 100.00% Assets:Checking + 100.00% Expenses:Travel + 92.14958953% Airfare + 3.12882793% Auto + 4.72158253% Passport + 100.00% Liabilities:MasterCard +>>>2 +=== 0 diff --git a/test/input/drewr.dat b/test/input/drewr.dat index 937daa5c..13b88844 100644 --- a/test/input/drewr.dat +++ b/test/input/drewr.dat @@ -57,3 +57,7 @@ 2004/01/27 Book Store Expenses:Books $20.00 Liabilities:MasterCard + +2004/02/01 Sale + Assets:Checking:Business $ 30.00 + Income:Sales diff --git a/test/regress/04C5E1CA.test b/test/regress/04C5E1CA.test new file mode 100644 index 00000000..729ae6bf --- /dev/null +++ b/test/regress/04C5E1CA.test @@ -0,0 +1,18 @@ +reg +<<< +2009/04/04 CS Club Sign + Expenses:School:CS Club:Home Depot:4" Brush 2 @ $3.97 + Liabilities:Mastercard + +2009/04/04 CS Club Sign + Expenses:School:CS Club:Home Depot:4" Brush (2 * $3.97) + Liabilities:Mastercard +>>>1 +09-Apr-04 CS Club Sign Ex:Sc:CS:Ho:4" Brush 2 2 + Liabilities:Mastercard $-7.94 2 + $-7.94 +09-Apr-04 CS Club Sign Ex:Sc:CS:Ho:4" Brush $7.94 2 + Liabilities:Mastercard $-7.94 2 + $-7.94 +>>>2 +=== 0 diff --git a/test/regress/5A03CFC3.test b/test/regress/5A03CFC3.test new file mode 100644 index 00000000..a5a12af3 --- /dev/null +++ b/test/regress/5A03CFC3.test @@ -0,0 +1,72 @@ +bal assets +<<< += account =~ /^Income/ + (Liabilities:Tithe) 0.12 + +~ Monthly + Assets:Checking $500.00 + Income:Salary + +2003/12/01 * Checking balance + Assets:Checking $1,000.00 + Equity:Opening Balances + +2003/12/20 Organic Co-op + Expenses:Food:Groceries $ 37.50 ; [=2004/01/01] + Expenses:Food:Groceries $ 37.50 ; [=2004/02/01] + Expenses:Food:Groceries $ 37.50 ; [=2004/03/01] + Expenses:Food:Groceries $ 37.50 ; [=2004/04/01] + Expenses:Food:Groceries $ 37.50 ; [=2004/05/01] + Expenses:Food:Groceries $ 37.50 ; [=2004/06/01] + Assets:Checking $ -225.00 + +2003/12/28=2004/01/01 Acme Mortgage + Liabilities:Mortgage:Principal $ 200.00 + Expenses:Interest:Mortgage $ 500.00 + Expenses:Escrow $ 300.00 + Assets:Checking $ -1000.00 + +2004/01/02 Grocery Store + Expenses:Food:Groceries $ 65.00 + Assets:Checking + +2004/01/05 Employer + Assets:Checking $ 2000.00 + Income:Salary + +2004/01/14 Bank + ; Regular monthly savings transfer + Assets:Savings $ 300.00 + Assets:Checking + +2004/01/19 Grocery Store + Expenses:Food:Groceries $ 44.00 + Assets:Checking + +2004/01/25 Bank + ; Transfer to cover car purchase + Assets:Checking $ 5,500.00 + Assets:Savings + ; :nobudget: + +2004/01/25 Tom's Used Cars + Expenses:Auto $ 5,500.00 + ; :nobudget: + Assets:Checking + +2004/01/27 Book Store + Expenses:Books $20.00 + Liabilities:MasterCard + +2004/02/01 Sale + Assets:Checking:Business $ 30.00 + Income:Sales +>>>1 + $ -3,804.00 Assets + $ 1,396.00 Checking + $ 30.00 Business + $ -5,200.00 Savings +-------------------- + $ -3,804.00 +>>>2 +=== 0 diff --git a/test/regress/5FBF2ED8.test b/test/regress/5FBF2ED8.test new file mode 100644 index 00000000..78df5a6e --- /dev/null +++ b/test/regress/5FBF2ED8.test @@ -0,0 +1,20 @@ +bal -B +<<< +2008/01/01 * Checking balance + Assets:Bank:Checking £0.00 + Equity:Opening Balances + +2008/02/02 Salary + Income:Employer £-334.00 + Assets:Bank:Checking $512.85 @@ £334.00 + +2008/03/02 Salary + Income:Employer £-248.07 + Assets:Bank:Checking $404.82 @@ £248.07 +>>>1 + £582.07 Assets:Bank:Checking + £-582.07 Income:Employer +-------------------- + 0 +>>>2 +=== 0 diff --git a/test/regress/647D5DB9.test b/test/regress/647D5DB9.test new file mode 100644 index 00000000..2d6b78e3 --- /dev/null +++ b/test/regress/647D5DB9.test @@ -0,0 +1,17 @@ +bal --end 2008/12/31 -JV Equities +<<< +2008/01/01 * Purchase Apple shares + Equities 1000 AAPL @ $2 + Cash + +2008/06/30 * Sell some Apple shares + Equities -500 AAPL @ $2.5 + Cash + +P 2008/10/01 02:18:02 AAPL $3 +P 2009/01/31 02:18:02 AAPL $4 +P 3000/01/01 02:18:02 APPL $100 +>>>1 +2008-12-31 1500 +>>>2 +=== 0 diff --git a/test/regress/6DAB9FE3.test b/test/regress/6DAB9FE3.test new file mode 100644 index 00000000..50b944ae --- /dev/null +++ b/test/regress/6DAB9FE3.test @@ -0,0 +1,10 @@ +reg -E +<<< +2009/01/01 Sample + assets 0 FOO @ $8.88 + equity +>>>1 +09-Jan-01 Sample assets 0 0 + equity 0 0 +>>>2 +=== 0 diff --git a/test/regress/727B2DF8.test b/test/regress/727B2DF8.test new file mode 100644 index 00000000..599090a1 --- /dev/null +++ b/test/regress/727B2DF8.test @@ -0,0 +1,81 @@ +reg --color +<<< +N $ + += account =~ /^Expenses:Books/ + (Liabilities:Taxes) -0.10 + +~ Monthly + Assets:Bank:Checking $500.00 + Income:Salary + +2004/05/01 * Checking balance + Assets:Bank:Checking $1,000.00 + Equity:Opening Balances + +2004/05/03=2004/05/01 * Investment balance + Assets:Brokerage 50 AAPL @ $30.00 + Equity:Opening Balances + +2004/05/14 * Páy dày + Assets:Bank:Checking 500.00€ + Income:Salary + +2004/05/14 * Another dày in which there is Páying + Asséts:Bánk:Chécking:Asséts:Bánk:Chécking $500.00 + Income:Salary + +2004/05/14 * Another dày in which there is Páying + Русский язык:Русский язык:Русский язык:Русский язык $1000.00 + Income:Salary + +2004/05/27 Book Store + Expenses:Books $20.00 + Expenses:Cards $40.00 + Expenses:Docs $30.00 + Liabilities:MasterCard + +2004/05/27 (100) Credit card company + ; This is an xact note! + ; Sample: Value + Liabilities:MasterCard $20.00 + ; This is a posting note! + ; Sample: Another Value + ; :MyTag: + Assets:Bank:Checking + ; :AnotherTag: +>>>1 +04-May-01 Checking balance [34mAssets:Bank:Checking [0m $1,000.00 $1,000.00 + [34mEq:Opening Balances [0m [31m$-1,000.00[0m 0 +04-May-03 Investment balance [34mAssets:Brokerage [0m 50 AAPL 50 AAPL + [34mEq:Opening Balances [0m [31m$-1,500.00[0m [31m$-1,500.00[0m + 50 AAPL +04-May-14 Páy dày [34mAssets:Bank:Checking [0m 500.00€ [31m$-1,500.00[0m + 50 AAPL + 500.00€ + [34mIncome:Salary [0m [31m-500.00€[0m [31m$-1,500.00[0m + 50 AAPL +04-May-14 Another dày in whic.. [34m..Bá:Ch:As:Bá:Chécking[0m $500.00 [31m$-1,000.00[0m + 50 AAPL + [34mIncome:Salary [0m [31m$-500.00[0m [31m$-1,500.00[0m + 50 AAPL +04-May-14 Another dày in whic.. [34mРу:Ру:Ру:Русский язык [0m $1,000.00 [31m$-500.00[0m + 50 AAPL + [34mIncome:Salary [0m [31m$-1,000.00[0m [31m$-1,500.00[0m + 50 AAPL +04-May-27 [1mBook Store [0m [34mExpenses:Books [0m $20.00 [31m$-1,480.00[0m + 50 AAPL + [34mExpenses:Cards [0m $40.00 [31m$-1,440.00[0m + 50 AAPL + [34mExpenses:Docs [0m $30.00 [31m$-1,410.00[0m + 50 AAPL + [34mLiabilities:MasterCard[0m [31m$-90.00[0m [31m$-1,500.00[0m + 50 AAPL + [34m(Liabilities:Taxes) [0m [31m$-2.00[0m [31m$-1,502.00[0m + 50 AAPL +04-May-27 [1mCredit card company [0m [34mLiabilities:MasterCard[0m $20.00 [31m$-1,482.00[0m + 50 AAPL + [34mAssets:Bank:Checking [0m [31m$-20.00[0m [31m$-1,502.00[0m + 50 AAPL +>>>2 +=== 0 diff --git a/test/regress/793F6BF0.test b/test/regress/793F6BF0.test new file mode 100644 index 00000000..a4e1234c --- /dev/null +++ b/test/regress/793F6BF0.test @@ -0,0 +1,52 @@ +entry 2009/03/15 book 10 +<<< +N $ + += account =~ /^Expenses:Books/ + (Liabilities:Taxes) -0.10 + +~ Monthly + Assets:Bank:Checking $500.00 + Income:Salary + +2004/05/01 * Checking balance + Assets:Bank:Checking $1,000.00 + Equity:Opening Balances + +2004/05/03=2004/05/01 * Investment balance + Assets:Brokerage 50 AAPL @ $30.00 + Equity:Opening Balances + +2004/05/14 * Páy dày + Assets:Bank:Checking 500.00€ + Income:Salary + +2004/05/14 * Another dày in which there is Páying + Asséts:Bánk:Chécking:Asséts:Bánk:Chécking $500.00 + Income:Salary + +2004/05/14 * Another dày in which there is Páying + Русский язык:Русский язык:Русский язык:Русский язык $1000.00 + Income:Salary + +2004/05/27 Book Store + Expenses:Books $20.00 + Expenses:Cards $40.00 + Expenses:Docs $30.00 + Liabilities:MasterCard + +2004/05/27 (100) Credit card company + ; This is an xact note! + ; Sample: Value + Liabilities:MasterCard $20.00 + ; This is a posting note! + ; Sample: Another Value + ; :MyTag: + Assets:Bank:Checking + ; :AnotherTag: +>>>1 +2009/03/15 Book Store + Expenses:Books $10.00 + Liabilities:MasterCard +>>>2 +=== 0 diff --git a/test/regress/86D2BDC4.test b/test/regress/86D2BDC4.test new file mode 100644 index 00000000..8cd51e42 --- /dev/null +++ b/test/regress/86D2BDC4.test @@ -0,0 +1,12 @@ +reg -B +<<< +2009/06/03 Westjet + Expenses:Transportation:Air C$429.80 @ 1.572865 + Expenses:Bank:Fees 2.73 + Liabilities:Mastercard +>>>1 +09-Jun-03 Westjet Ex:Transportation:Air 676.017377 676.017377 + Expenses:Bank:Fees 2.73 678.747377 + Liabilities:Mastercard -678.747377 0 +>>>2 +=== 0 diff --git a/test/regress/B68FFB0D.test b/test/regress/B68FFB0D.test new file mode 100644 index 00000000..3a43df4c --- /dev/null +++ b/test/regress/B68FFB0D.test @@ -0,0 +1,15 @@ +print +<<< +D $1,000.00 + +2009/01/01 Sample + assets 134.123 FOO @ $8.88 + assets 100 BAR @ $8.88 + equity +>>>1 +2009/01/01 Sample + assets 134.123 FOO @ $8.88 + assets 100 BAR @ $8.88 + equity +>>>2 +=== 0 diff --git a/test/regress/BBFA1759.test b/test/regress/BBFA1759.test new file mode 100644 index 00000000..ca6842dc --- /dev/null +++ b/test/regress/BBFA1759.test @@ -0,0 +1,10 @@ +period june 2008 +<<< +>>>1 +global details => + + start: 2008-06-01 + end: 2008-07-01 + factor: 1 +>>>2 +=== 0 diff --git a/test/regress/C523E23F.test b/test/regress/C523E23F.test new file mode 100644 index 00000000..b8b08e00 --- /dev/null +++ b/test/regress/C523E23F.test @@ -0,0 +1,21 @@ +bal +<<< +D 1.000,00€ + +2009/03/16 * denn's + ; Kauf: Yogi-Tee + Aufwand:Einkauf:Lebensmittel 17,94€ + Aktiva:Bank:Girokonto + +2009/03/24 Ansparen + Aktiva:Bank:Sparkonto 800,00€ + Aktiva:Bank:Girokonto +>>>1 + -17,94€ Aktiva:Bank + -817,94€ Girokonto + 800,00€ Sparkonto + 17,94€ Aufwand:Einkauf:Lebensmittel +-------------------- + 0 +>>>2 +=== 0 diff --git a/test/regress/D943AE0F.test b/test/regress/D943AE0F.test new file mode 100644 index 00000000..3e568db6 --- /dev/null +++ b/test/regress/D943AE0F.test @@ -0,0 +1,13 @@ +reg -V --end=2009/06/16 +<<< +2008/04/15 * Paid expenses back from cie. + Expenses:Cie-Reimbursements 2000 CAD @ 1.10 EUR + Assets:Checking + +P 2008/04/20 00:00:00 CAD 1.20 EUR +>>>1 +08-Apr-15 Paid expenses back .. Ex:Cie-Reimbursements 2200.00 EUR 2200.00 EUR + Assets:Checking -2200.00 EUR 0 +09-Jun-16 Commodities revalued <Revalued> 200.00 EUR 200.00 EUR +>>>2 +=== 0 diff --git a/test/regress/F559EC12.test b/test/regress/F559EC12.test new file mode 100644 index 00000000..7900b60d --- /dev/null +++ b/test/regress/F559EC12.test @@ -0,0 +1,35 @@ +format "%-12(scrub(amount))" +<<< +>>>1 +--- Context is first posting of the following transaction --- +2004/05/27 Book Store + Expenses:Books 20 BOOK @ $10 + Liabilities:MasterCard $-200.00 + +--- Input format string --- +%-12(scrub(amount)) + +--- Format elements --- +Element: EXPR flags: 0x1 min: 12 max: 0 expr: scrub(amount) + +--- Formatted string --- +"20 BOOK " +>>>2 +=== 0 +format "%12(scrub(amount))" +>>>1 +--- Context is first posting of the following transaction --- +2004/05/27 Book Store + Expenses:Books 20 BOOK @ $10 + Liabilities:MasterCard $-200.00 + +--- Input format string --- +%12(scrub(amount)) + +--- Format elements --- +Element: EXPR flags: 0x0 min: 12 max: 0 expr: scrub(amount) + +--- Formatted string --- +" 20 BOOK" +>>>2 +=== 0 diff --git a/test/unit/t_amount.cc b/test/unit/t_amount.cc index 8aa67137..ba863ca7 100644 --- a/test/unit/t_amount.cc +++ b/test/unit/t_amount.cc @@ -866,7 +866,7 @@ void AmountTestCase::testIntegerDivision() x1 /= amount_t(456L); assertEqual(string("0.269737"), x1.to_string()); x1 /= 456L; - assertEqual(string("0.000591528162511542"), x1.to_string()); + assertEqual(string("0.000591528163"), x1.to_string()); amount_t x4("123456789123456789123456789"); amount_t y4("56"); @@ -889,32 +889,32 @@ void AmountTestCase::testFractionalDivision() amount_t y1("456.456"); assertThrow(x1 / 0L, amount_error); - assertEqual(string("0.00812195934"), (amount_t("1.0") / x1).to_string()); - assertEqual(string("0.00812195934"), (amount_t("1.0") / x1).to_string()); + assertEqual(string("0.0081219593"), (amount_t("1.0") / x1).to_string()); + assertEqual(string("0.0081219593"), (amount_t("1.0") / x1).to_string()); assertEqual(x1, x1 / amount_t("1.0")); - assertEqual(string("0.00812195934"), (amount_t("1.0") / x1).to_string()); - assertEqual(string("0.00812195934"), (amount_t("1.0") / x1).to_string()); + assertEqual(string("0.0081219593"), (amount_t("1.0") / x1).to_string()); + assertEqual(string("0.0081219593"), (amount_t("1.0") / x1).to_string()); assertEqual(- x1, x1 / amount_t("-1.0")); - assertEqual(string("-0.00812195934"), (amount_t("-1.0") / x1).to_string()); - assertEqual(string("-0.00812195934"), (amount_t("-1.0") / x1).to_string()); - assertEqual(string("0.269736842105263"), (x1 / y1).to_string()); - assertEqual(string("3.707317073170732"), (y1 / x1).to_string()); - assertEqual(string("0.269736842105263"), (x1 / amount_t("456.456")).to_string()); - assertEqual(string("3.707317073170732"), (amount_t("456.456") / x1).to_string()); - assertEqual(string("3.707317073170732"), (amount_t("456.456") / x1).to_string()); + assertEqual(string("-0.0081219593"), (amount_t("-1.0") / x1).to_string()); + assertEqual(string("-0.0081219593"), (amount_t("-1.0") / x1).to_string()); + assertEqual(string("0.269736842105"), (x1 / y1).to_string()); + assertEqual(string("3.707317073171"), (y1 / x1).to_string()); + assertEqual(string("0.269736842105"), (x1 / amount_t("456.456")).to_string()); + assertEqual(string("3.707317073171"), (amount_t("456.456") / x1).to_string()); + assertEqual(string("3.707317073171"), (amount_t("456.456") / x1).to_string()); x1 /= amount_t("456.456"); - assertEqual(string("0.269736842105263"), x1.to_string()); + assertEqual(string("0.269736842105"), x1.to_string()); x1 /= amount_t("456.456"); - assertEqual(string("0.000590937225286255757169884601508201951"), x1.to_string()); + assertEqual(string("0.000590937225286255757"), x1.to_string()); x1 /= 456L; - assertEqual(string("0.00000129591496773301701133746621429767819329289006668733529828959526392431755859036"), x1.to_string()); + assertEqual(string("0.000001295914967733017011337"), x1.to_string()); amount_t x4("1234567891234567.89123456789"); amount_t y4("56.789"); assertEqual(amount_t("1.0"), x4 / x4); - assertEqual(string("21739560323910.7554497273748437197344556164046"), (x4 / y4).to_string()); + assertEqual(string("21739560323910.75544972737484371973"), (x4 / y4).to_string()); assertValid(x1); assertValid(y1); @@ -946,9 +946,9 @@ void AmountTestCase::testCommodityDivision() // Internal amounts retain their precision, even when being // converted to strings assertEqual(string("$0.99727201"), (x1 / x2).to_fullstring()); - assertEqual(string("$1.00273545321637426901"), (x2 / x1).to_fullstring()); + assertEqual(string("$1.00273545321637"), (x2 / x1).to_fullstring()); assertEqual(string("$1.00"), (x1 / x2).to_string()); - assertEqual(string("$1.00273545321637426901"), (x2 / x1).to_string()); + assertEqual(string("$1.00273545321637"), (x2 / x1).to_string()); assertThrow(x1 / x0, amount_error); assertThrow(x0 / x1, amount_error); @@ -970,9 +970,9 @@ void AmountTestCase::testCommodityDivision() amount_t x7(internalAmount("$123456789123456789.123456789123456789")); assertEqual(amount_t("$1"), x7 / x7); - assertEqual(string("$0.0019216115121765559608381226612019501046413574469262"), + assertEqual(string("$0.0019216115121765559608381226612019501"), (x6 / x7).to_fullstring()); - assertEqual(string("$520.39654928343335571379527154924040947271699678158689736256"), + assertEqual(string("$520.39654928343335571379527154924040947272"), (x7 / x6).to_fullstring()); assertValid(x1); |