From c28d828d8e24fe637a74674bedc9bc0cbdabca1c Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 17 Nov 2009 21:30:07 -0500 Subject: Renamed date_interval_t::end to finish --- src/filters.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/filters.cc') diff --git a/src/filters.cc b/src/filters.cc index bef4dc24..5a92421b 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -784,7 +784,7 @@ void budget_posts::report_budget_items(const date_t& date) assert(begin); if (*begin <= date && - (! pair.first.end || *begin < *pair.first.end)) { + (! pair.first.finish || *begin < *pair.first.finish)) { post_t& post = *pair.second; DEBUG("budget.generate", "Reporting budget for " @@ -905,8 +905,8 @@ void forecast_posts::flush() } date_t& begin = *(*least).first.start; - if ((*least).first.end) - assert(begin < *(*least).first.end); + if ((*least).first.finish) + assert(begin < *(*least).first.finish); // If the next date in the series for this periodic posting is more than 5 // years beyond the last valid post we generated, drop it from further -- cgit v1.2.3 From e4b3f0bb3a74b799f0f67d8b2f1efeedad5e2021 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 18 Nov 2009 05:45:48 -0500 Subject: The new period parser is passing all tests --- src/archive.cc | 2 +- src/filters.cc | 14 ++- src/precmd.cc | 2 +- src/report.cc | 13 ++- src/report.h | 31 ++--- src/times.cc | 275 ++++++++++++++++++++++++++++----------------- src/times.h | 18 +-- src/token.cc | 5 +- test/regress/7F3650FD.test | 69 ++++++++++-- test/regress/BBFA1759.test | 14 ++- 10 files changed, 283 insertions(+), 160 deletions(-) (limited to 'src/filters.cc') diff --git a/src/archive.cc b/src/archive.cc index d36712ca..7306f8d3 100644 --- a/src/archive.cc +++ b/src/archive.cc @@ -43,7 +43,7 @@ #include "xact.h" #define LEDGER_MAGIC 0x4c454447 -#define ARCHIVE_VERSION 0x03000005 +#define ARCHIVE_VERSION 0x03000006 //BOOST_IS_ABSTRACT(ledger::scope_t) BOOST_CLASS_EXPORT(ledger::scope_t) diff --git a/src/filters.cc b/src/filters.cc index 5a92421b..47e7f66d 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -515,12 +515,14 @@ void subtotal_posts::report_subtotal(const char * spec_fmt, optional range_start = interval ? interval->start : none; optional range_finish = interval ? interval->inclusive_end() : none; - foreach (post_t * post, component_posts) { - date_t date = post->date(); - if (! range_start || date < *range_start) - range_start = date; - if (! range_finish || date > *range_finish) - range_finish = date; + if (! range_start || ! range_finish) { + foreach (post_t * post, component_posts) { + date_t date = post->date(); + if (! range_start || date < *range_start) + range_start = date; + if (! range_finish || date > *range_finish) + range_finish = date; + } } component_posts.clear(); diff --git a/src/precmd.cc b/src/precmd.cc index 0e024a39..516b90dd 100644 --- a/src/precmd.cc +++ b/src/precmd.cc @@ -169,7 +169,7 @@ value_t period_command(call_scope_t& args) out << std::endl; date_interval_t interval(arg); - interval.dump(out); + interval.dump(out, report.session.current_year); return NULL_VALUE; } diff --git a/src/report.cc b/src/report.cc index efe162e2..78ed05c0 100644 --- a/src/report.cc +++ b/src/report.cc @@ -120,14 +120,15 @@ void report_t::normalize_options(const string& verb) date_interval_t interval(HANDLER(period_).str()); - if (! HANDLED(begin_) && interval.start) { - string predicate = - "date>=[" + to_iso_extended_string(*interval.start) + "]"; + optional begin = interval.begin(session.current_year); + optional end = interval.end(session.current_year); + + if (! HANDLED(begin_) && begin) { + string predicate = "date>=[" + to_iso_extended_string(*begin) + "]"; HANDLER(limit_).on(string("?normalize"), predicate); } - if (! HANDLED(end_) && interval.finish) { - string predicate = - "date<[" + to_iso_extended_string(*interval.finish) + "]"; + if (! HANDLED(end_) && end) { + string predicate = "date<[" + to_iso_extended_string(*end) + "]"; HANDLER(limit_).on(string("?normalize"), predicate); } diff --git a/src/report.h b/src/report.h index 93f6e9e0..0b1baff1 100644 --- a/src/report.h +++ b/src/report.h @@ -50,6 +50,7 @@ #include "option.h" #include "commodity.h" #include "annotate.h" +#include "session.h" #include "format.h" namespace ledger { @@ -352,7 +353,7 @@ public: set_expr(args[0].to_string(), args[1].to_string()); }); - OPTION(report_t, amount_data); + OPTION(report_t, amount_data); // -j OPTION(report_t, anon); OPTION_(report_t, average, DO() { // -A @@ -377,14 +378,14 @@ public: }); OPTION_(report_t, begin_, DO_(args) { // -b - date_interval_t interval(args[1].to_string()); - if (! interval.start) + date_interval_t interval(args[1].to_string()); + optional begin = interval.begin(parent->session.current_year); + if (! begin) throw_(std::invalid_argument, _("Could not determine beginning of period '%1'") << args[1].to_string()); - string predicate = - "date>=[" + to_iso_extended_string(*interval.start) + "]"; + string predicate = "date>=[" + to_iso_extended_string(*begin) + "]"; parent->HANDLER(limit_).on(string("--begin"), predicate); }); @@ -524,17 +525,19 @@ public: OPTION(report_t, empty); // -E OPTION_(report_t, end_, DO_(args) { // -e - date_interval_t interval(args[1].to_string()); - if (! interval.start) + date_interval_t interval(args[1].to_string()); + // Use begin() here so that if the user says --end=2008, we end on + // 2008/01/01 instead of 2009/01/01 (which is what end() would return). + optional end = interval.begin(parent->session.current_year); + if (! end) throw_(std::invalid_argument, _("Could not determine end of period '%1'") << args[1].to_string()); - string predicate = - "date<[" + to_iso_extended_string(*interval.start) + "]"; + string predicate = "date<[" + to_iso_extended_string(*end) + "]"; parent->HANDLER(limit_).on(string("--end"), predicate); - parent->terminus = datetime_t(*interval.start); + parent->terminus = datetime_t(*end); }); OPTION(report_t, equity); @@ -622,11 +625,13 @@ public: OPTION_(report_t, now_, DO_(args) { date_interval_t interval(args[1].to_string()); - if (! interval.start) + optional begin = interval.begin(parent->session.current_year); + if (! begin) throw_(std::invalid_argument, _("Could not determine beginning of period '%1'") << args[1].to_string()); - ledger::epoch = datetime_t(*interval.start); + ledger::epoch = parent->terminus = datetime_t(*begin); + parent->session.current_year = ledger::epoch->date().year(); }); OPTION__ @@ -844,7 +849,7 @@ public: set_expr(args[0].to_string(), args[1].to_string()); }); - OPTION(report_t, total_data); + OPTION(report_t, total_data); // -J OPTION_(report_t, truncate_, DO_(args) { string style(args[1].to_string()); diff --git a/src/times.cc b/src/times.cc index 97fc3ab8..646f2a84 100644 --- a/src/times.cc +++ b/src/times.cc @@ -434,6 +434,7 @@ class date_parser_t } kind; typedef variant value; explicit token_t(kind_t _kind = UNKNOWN, - const optional& _value = none) + const optional& _value = + content_t(empty_string)) : kind(_kind), value(_value) { TRACE_CTOR(date_parser_t::lexer_t::token_t, ""); } @@ -467,47 +469,110 @@ class date_parser_t } string to_string() const { + std::ostringstream out; + + switch (kind) { + case UNKNOWN: + out << boost::get(*value); + break; + case TOK_DATE: + return boost::get(*value).to_string(); + case TOK_INT: + out << boost::get(*value); + break; + case TOK_SLASH: return "/"; + case TOK_DASH: return "-"; + case TOK_DOT: return "."; + case TOK_A_YEAR: + out << boost::get(*value); + break; + case TOK_A_MONTH: + out << date_specifier_t::month_type + (boost::get(*value)); + break; + case TOK_A_WDAY: + out << date_specifier_t::day_of_week_type + (boost::get(*value)); + break; + case TOK_SINCE: return "since"; + case TOK_UNTIL: return "until"; + case TOK_IN: return "in"; + case TOK_THIS: return "this"; + case TOK_NEXT: return "next"; + case TOK_LAST: return "last"; + case TOK_EVERY: return "every"; + case TOK_TODAY: return "today"; + case TOK_TOMORROW: return "tomorrow"; + case TOK_YESTERDAY: return "yesterday"; + case TOK_YEAR: return "year"; + case TOK_QUARTER: return "quarter"; + case TOK_MONTH: return "month"; + case TOK_WEEK: return "week"; + case TOK_DAY: return "day"; + case TOK_YEARLY: return "yearly"; + case TOK_QUARTERLY: return "quarterly"; + case TOK_BIMONTHLY: return "bimonthly"; + case TOK_MONTHLY: return "monthly"; + case TOK_BIWEEKLY: return "biweekly"; + case TOK_WEEKLY: return "weekly"; + case TOK_DAILY: return "daily"; + case TOK_YEARS: return "years"; + case TOK_QUARTERS: return "quarters"; + case TOK_MONTHS: return "months"; + case TOK_WEEKS: return "weeks"; + case TOK_DAYS: return "days"; + case END_REACHED: return ""; + default: + assert(false); + return empty_string; + } + + return out.str(); + } + + void dump(std::ostream& out) const { switch (kind) { - case UNKNOWN: return "UNKNOWN"; - case TOK_DATE: return "TOK_DATE"; - case TOK_INT: return "TOK_INT"; - case TOK_SLASH: return "TOK_SLASH"; - case TOK_DASH: return "TOK_DASH"; - case TOK_DOT: return "TOK_DOT"; - case TOK_A_YEAR: return "TOK_A_YEAR"; - case TOK_A_MONTH: return "TOK_A_MONTH"; - case TOK_A_WDAY: return "TOK_A_WDAY"; - case TOK_SINCE: return "TOK_SINCE"; - case TOK_UNTIL: return "TOK_UNTIL"; - case TOK_IN: return "TOK_IN"; - case TOK_THIS: return "TOK_THIS"; - case TOK_NEXT: return "TOK_NEXT"; - case TOK_LAST: return "TOK_LAST"; - case TOK_EVERY: return "TOK_EVERY"; - case TOK_TODAY: return "TOK_EVERY"; - case TOK_TOMORROW: return "TOK_TOMORROW"; - case TOK_YESTERDAY: return "TOK_YESTERDAY"; - case TOK_YEAR: return "TOK_YEAR"; - case TOK_QUARTER: return "TOK_QUARTER"; - case TOK_MONTH: return "TOK_MONTH"; - case TOK_WEEK: return "TOK_WEEK"; - case TOK_DAY: return "TOK_DAY"; - case TOK_YEARLY: return "TOK_YEARLY"; - case TOK_QUARTERLY: return "TOK_QUARTERLY"; - case TOK_BIMONTHLY: return "TOK_BIMONTHLY"; - case TOK_MONTHLY: return "TOK_MONTHLY"; - case TOK_BIWEEKLY: return "TOK_BIWEEKLY"; - case TOK_WEEKLY: return "TOK_WEEKLY"; - case TOK_DAILY: return "TOK_DAILY"; - case TOK_YEARS: return "TOK_YEARS"; - case TOK_QUARTERS: return "TOK_QUARTERS"; - case TOK_MONTHS: return "TOK_MONTHS"; - case TOK_WEEKS: return "TOK_WEEKS"; - case TOK_DAYS: return "TOK_DAYS"; - case END_REACHED: return "END_REACHED"; + case UNKNOWN: out << "UNKNOWN"; break; + case TOK_DATE: out << "TOK_DATE"; break; + case TOK_INT: out << "TOK_INT"; break; + case TOK_SLASH: out << "TOK_SLASH"; break; + case TOK_DASH: out << "TOK_DASH"; break; + case TOK_DOT: out << "TOK_DOT"; break; + case TOK_A_YEAR: out << "TOK_A_YEAR"; break; + case TOK_A_MONTH: out << "TOK_A_MONTH"; break; + case TOK_A_WDAY: out << "TOK_A_WDAY"; break; + case TOK_SINCE: out << "TOK_SINCE"; break; + case TOK_UNTIL: out << "TOK_UNTIL"; break; + case TOK_IN: out << "TOK_IN"; break; + case TOK_THIS: out << "TOK_THIS"; break; + case TOK_NEXT: out << "TOK_NEXT"; break; + case TOK_LAST: out << "TOK_LAST"; break; + case TOK_EVERY: out << "TOK_EVERY"; break; + case TOK_TODAY: out << "TOK_TODAY"; break; + case TOK_TOMORROW: out << "TOK_TOMORROW"; break; + case TOK_YESTERDAY: out << "TOK_YESTERDAY"; break; + case TOK_YEAR: out << "TOK_YEAR"; break; + case TOK_QUARTER: out << "TOK_QUARTER"; break; + case TOK_MONTH: out << "TOK_MONTH"; break; + case TOK_WEEK: out << "TOK_WEEK"; break; + case TOK_DAY: out << "TOK_DAY"; break; + case TOK_YEARLY: out << "TOK_YEARLY"; break; + case TOK_QUARTERLY: out << "TOK_QUARTERLY"; break; + case TOK_BIMONTHLY: out << "TOK_BIMONTHLY"; break; + case TOK_MONTHLY: out << "TOK_MONTHLY"; break; + case TOK_BIWEEKLY: out << "TOK_BIWEEKLY"; break; + case TOK_WEEKLY: out << "TOK_WEEKLY"; break; + case TOK_DAILY: out << "TOK_DAILY"; break; + case TOK_YEARS: out << "TOK_YEARS"; break; + case TOK_QUARTERS: out << "TOK_QUARTERS"; break; + case TOK_MONTHS: out << "TOK_MONTHS"; break; + case TOK_WEEKS: out << "TOK_WEEKS"; break; + case TOK_DAYS: out << "TOK_DAYS"; break; + case END_REACHED: out << "END_REACHED"; break; + default: + assert(false); + break; } - assert(false); - return empty_string; } void unexpected(); @@ -753,7 +818,9 @@ date_interval_t date_parser_t::parse() inclusion_specifier = date_specifier_t(static_cast(temp.year()), temp.month()); +#if 0 period.duration = date_duration_t(date_duration_t::QUARTERS, 1); +#endif break; } @@ -771,7 +838,9 @@ date_interval_t date_parser_t::parse() date_duration_t::find_nearest(today, date_duration_t::WEEKS); temp += gregorian::days(7 * adjust); inclusion_specifier = date_specifier_t(today); +#if 0 period.duration = date_duration_t(date_duration_t::WEEKS, 1); +#endif break; } @@ -806,19 +875,19 @@ date_interval_t date_parser_t::parse() tok = lexer.next_token(); switch (tok.kind) { case lexer_t::token_t::TOK_YEARS: - period.skip_duration = date_duration_t(date_duration_t::YEARS, quantity); + period.duration = date_duration_t(date_duration_t::YEARS, quantity); break; case lexer_t::token_t::TOK_QUARTERS: - period.skip_duration = date_duration_t(date_duration_t::QUARTERS, quantity); + period.duration = date_duration_t(date_duration_t::QUARTERS, quantity); break; case lexer_t::token_t::TOK_MONTHS: - period.skip_duration = date_duration_t(date_duration_t::MONTHS, quantity); + period.duration = date_duration_t(date_duration_t::MONTHS, quantity); break; case lexer_t::token_t::TOK_WEEKS: - period.skip_duration = date_duration_t(date_duration_t::WEEKS, quantity); + period.duration = date_duration_t(date_duration_t::WEEKS, quantity); break; case lexer_t::token_t::TOK_DAYS: - period.skip_duration = date_duration_t(date_duration_t::DAYS, quantity); + period.duration = date_duration_t(date_duration_t::DAYS, quantity); break; default: tok.unexpected(); @@ -827,19 +896,19 @@ date_interval_t date_parser_t::parse() } else { switch (tok.kind) { case lexer_t::token_t::TOK_YEAR: - period.skip_duration = date_duration_t(date_duration_t::YEARS, 1); + period.duration = date_duration_t(date_duration_t::YEARS, 1); break; case lexer_t::token_t::TOK_QUARTER: - period.skip_duration = date_duration_t(date_duration_t::QUARTERS, 1); + period.duration = date_duration_t(date_duration_t::QUARTERS, 1); break; case lexer_t::token_t::TOK_MONTH: - period.skip_duration = date_duration_t(date_duration_t::MONTHS, 1); + period.duration = date_duration_t(date_duration_t::MONTHS, 1); break; case lexer_t::token_t::TOK_WEEK: - period.skip_duration = date_duration_t(date_duration_t::WEEKS, 1); + period.duration = date_duration_t(date_duration_t::WEEKS, 1); break; case lexer_t::token_t::TOK_DAY: - period.skip_duration = date_duration_t(date_duration_t::DAYS, 1); + period.duration = date_duration_t(date_duration_t::DAYS, 1); break; default: tok.unexpected(); @@ -849,25 +918,25 @@ date_interval_t date_parser_t::parse() break; case lexer_t::token_t::TOK_YEARLY: - period.skip_duration = date_duration_t(date_duration_t::YEARS, 1); + period.duration = date_duration_t(date_duration_t::YEARS, 1); break; case lexer_t::token_t::TOK_QUARTERLY: - period.skip_duration = date_duration_t(date_duration_t::QUARTERS, 1); + period.duration = date_duration_t(date_duration_t::QUARTERS, 1); break; case lexer_t::token_t::TOK_BIMONTHLY: - period.skip_duration = date_duration_t(date_duration_t::MONTHS, 2); + period.duration = date_duration_t(date_duration_t::MONTHS, 2); break; case lexer_t::token_t::TOK_MONTHLY: - period.skip_duration = date_duration_t(date_duration_t::MONTHS, 1); + period.duration = date_duration_t(date_duration_t::MONTHS, 1); break; case lexer_t::token_t::TOK_BIWEEKLY: - period.skip_duration = date_duration_t(date_duration_t::WEEKS, 2); + period.duration = date_duration_t(date_duration_t::WEEKS, 2); break; case lexer_t::token_t::TOK_WEEKLY: - period.skip_duration = date_duration_t(date_duration_t::WEEKS, 1); + period.duration = date_duration_t(date_duration_t::WEEKS, 1); break; case lexer_t::token_t::TOK_DAILY: - period.skip_duration = date_duration_t(date_duration_t::DAYS, 1); + period.duration = date_duration_t(date_duration_t::DAYS, 1); break; default: @@ -876,8 +945,10 @@ date_interval_t date_parser_t::parse() } } +#if 0 if (! period.duration && inclusion_specifier) period.duration = inclusion_specifier->implied_duration(); +#endif if (since_specifier || until_specifier) { date_range_t range(since_specifier, until_specifier); @@ -915,16 +986,9 @@ void date_interval_t::resolve_end() "stabilize: end_of_duration reset to end: " << *end_of_duration); } - if (! skip_duration) { - skip_duration = duration; - DEBUG("times.interval", - "stabilize: skip_duration set to: " << *skip_duration); - } - if (start && ! next) { - next = skip_duration->add(*start); - DEBUG("times.interval", - "stabilize: next set to: " << *next); + next = end_of_duration; + DEBUG("times.interval", "stabilize: next set to: " << *next); } } @@ -988,10 +1052,10 @@ void date_interval_t::stabilize(const optional& date) #if defined(DEBUG_ON) if (initial_start) DEBUG("times.interval", - "stabilize: initial_start = " << *initial_start); + "stabilize: initial_start = " << *initial_start); if (initial_finish) DEBUG("times.interval", - "stabilize: initial_finish = " << *initial_finish); + "stabilize: initial_finish = " << *initial_finish); #endif date_t when = start ? *start : *date; @@ -1024,9 +1088,10 @@ void date_interval_t::stabilize(const optional& date) } } - DEBUG("times.interval", "stabilize: final start date = " << *start); + DEBUG("times.interval", "stabilize: proposed start date = " << *start); if (initial_start && (! start || *start < *initial_start)) { + // Using the discovered start, find the end of the period resolve_end(); start = initial_start; @@ -1036,14 +1101,20 @@ void date_interval_t::stabilize(const optional& date) finish = initial_finish; DEBUG("times.interval", "stabilize: finish reset to initial finish"); } + + if (start) + DEBUG("times.interval", "stabilize: final start = " << *start); + if (finish) + DEBUG("times.interval", "stabilize: final finish = " << *finish); } else if (range) { - start = range->begin(); - finish = range->end(); - - if (start && finish) - duration = date_duration_t(date_duration_t::DAYS, - static_cast((*finish - *start).days())); + if (date) { + start = range->begin(date->year()); + finish = range->end(date->year()); + } else { + start = range->begin(); + finish = range->end(); + } } aligned = true; } @@ -1116,7 +1187,7 @@ bool date_interval_t::find_period(const date_t& date) return true; } - scan = skip_duration->add(scan); + scan = duration->add(scan); end_of_scan = duration->add(scan); } @@ -1130,7 +1201,7 @@ date_interval_t& date_interval_t::operator++() stabilize(); - if (! skip_duration || ! duration) + if (! duration) throw_(date_error, _("Cannot increment a date interval without a duration")); @@ -1140,10 +1211,8 @@ date_interval_t& date_interval_t::operator++() start = none; } else { start = *next; - end_of_duration = duration->add(*start); } - next = none; resolve_end(); @@ -1151,7 +1220,7 @@ date_interval_t& date_interval_t::operator++() return *this; } -void date_interval_t::dump(std::ostream& out) +void date_interval_t::dump(std::ostream& out, optional_year current_year) { out << _("--- Before stabilization ---") << std::endl; @@ -1162,14 +1231,10 @@ void date_interval_t::dump(std::ostream& out) if (finish) out << _(" finish: ") << format_date(*finish) << std::endl; - if (skip_duration) - out << _(" skip: ") << skip_duration->to_string() << std::endl; - if (factor) - out << _(" factor: ") << factor << std::endl; if (duration) out << _("duration: ") << duration->to_string() << std::endl; - stabilize(begin()); + stabilize(begin(current_year)); out << std::endl << _("--- After stabilization ---") << std::endl; @@ -1181,27 +1246,30 @@ void date_interval_t::dump(std::ostream& out) if (finish) out << _(" finish: ") << format_date(*finish) << std::endl; - if (skip_duration) - out << _(" skip: ") << skip_duration->to_string() << std::endl; - if (factor) - out << _(" factor: ") << factor << std::endl; if (duration) out << _("duration: ") << duration->to_string() << std::endl; out << std::endl << _("--- Sample dates in range (max. 20) ---") << std::endl; - for (int i = 0; i < 20 && *this; i++, ++*this) { + date_t last_date; + + for (int i = 0; i < 20 && *this; ++i, ++*this) { out << std::right; out.width(2); + if (! last_date.is_not_a_date() && last_date == *start) + break; + out << (i + 1) << ": " << format_date(*start); - if (skip_duration) - out << " -- " << format_date(*inclusive_skip_end()); + if (duration) + out << " -- " << format_date(*inclusive_end()); out << std::endl; - if (! skip_duration) + if (! duration) break; + + last_date = *start; } } @@ -1268,6 +1336,8 @@ date_parser_t::lexer_t::token_t date_parser_t::lexer_t::next_token() token_t::content_t(lexical_cast(term))); } else if (std::isalpha(term[0])) { + to_lower(term); + if (optional month = string_to_month_of_year(term)) { return token_t(token_t::TOK_A_MONTH, token_t::content_t(*month)); @@ -1339,20 +1409,20 @@ date_parser_t::lexer_t::token_t date_parser_t::lexer_t::next_token() token_t::expected('\0', *begin); } - return token_t(token_t::UNKNOWN); + return token_t(token_t::UNKNOWN, token_t::content_t(term)); } void date_parser_t::lexer_t::token_t::unexpected() { - kind_t prev_kind = kind; - - kind = UNKNOWN; - - switch (prev_kind) { + switch (kind) { case END_REACHED: + kind = UNKNOWN; throw_(date_error, _("Unexpected end of expression")); - default: - throw_(date_error, _("Unexpected token '%1'") << to_string()); + default: { + string desc = to_string(); + kind = UNKNOWN; + throw_(date_error, _("Unexpected date period token '%1'") << desc); + } } } @@ -1503,7 +1573,8 @@ void show_period_tokens(std::ostream& out, const string& arg) date_parser_t::lexer_t::token_t token; do { token = lexer.next_token(); - out << token.to_string() << std::endl; + token.dump(out); + out << ": " << token.to_string() << std::endl; } while (token.kind != date_parser_t::lexer_t::token_t::END_REACHED); } diff --git a/src/times.h b/src/times.h index d378dd81..1c9d812e 100644 --- a/src/times.h +++ b/src/times.h @@ -534,16 +534,14 @@ public: optional start; // the real start, after adjustment optional finish; // the real end, likewise bool aligned; - optional skip_duration; - std::size_t factor; optional next; optional duration; optional end_of_duration; - explicit date_interval_t() : aligned(false), factor(1) { + explicit date_interval_t() : aligned(false) { TRACE_CTOR(date_interval_t, ""); } - date_interval_t(const string& str) : aligned(false), factor(1) { + date_interval_t(const string& str) : aligned(false) { TRACE_CTOR(date_interval_t, "const string&"); parse(str); } @@ -552,8 +550,6 @@ public: start(other.start), finish(other.finish), aligned(other.aligned), - skip_duration(other.skip_duration), - factor(other.factor), next(other.next), duration(other.duration), end_of_duration(other.end_of_duration) { @@ -593,12 +589,6 @@ public: containing date, or false if no such period can be found. */ bool find_period(const date_t& date); - optional inclusive_skip_end() const { - if (skip_duration) - return skip_duration->add(*start) - gregorian::days(1); - else - return none; - } optional inclusive_end() const { if (end_of_duration) return *end_of_duration - gregorian::days(1); @@ -608,7 +598,7 @@ public: date_interval_t& operator++(); - void dump(std::ostream& out); + void dump(std::ostream& out, optional_year current_year = none); #if defined(HAVE_BOOST_SERIALIZATION) private: @@ -622,8 +612,6 @@ private: ar & start; ar & finish; ar & aligned; - ar & skip_duration; - ar & factor; ar & next; ar & duration; ar & end_of_duration; diff --git a/src/token.cc b/src/token.cc index 3df072a7..81c54a82 100644 --- a/src/token.cc +++ b/src/token.cc @@ -206,11 +206,12 @@ void expr_t::token_t::next(std::istream& in, const parse_flags_t& pflags) length++; date_interval_t timespan(buf); - if (! timespan) + optional begin = timespan.begin(); + if (! begin) throw_(parse_error, _("Date specifier does not refer to a starting date")); kind = VALUE; - value = *timespan.start; + value = *begin; break; } diff --git a/test/regress/7F3650FD.test b/test/regress/7F3650FD.test index dce5233f..f7154eb8 100644 --- a/test/regress/7F3650FD.test +++ b/test/regress/7F3650FD.test @@ -1,50 +1,95 @@ period --now=2010/11/01 12/01 <<< >>>1 -global details => +--- Period expression tokens --- +TOK_DATE: month Dec day 1 +END_REACHED: - start: 09-Dec-01 - finish: 09-Dec-02 - factor: 1 +--- Before stabilization --- + range: in month Dec day 1 + +--- After stabilization --- + range: in month Dec day 1 + start: 10-Dec-01 + finish: 10-Dec-02 + +--- Sample dates in range (max. 20) --- + 1: 10-Dec-01 >>>2 === 0 period --now=2010/11/01 10/01 <<< >>>1 -global details => +--- Period expression tokens --- +TOK_DATE: month Oct day 1 +END_REACHED: + +--- Before stabilization --- + range: in month Oct day 1 +--- After stabilization --- + range: in month Oct day 1 start: 10-Oct-01 finish: 10-Oct-02 - factor: 1 + +--- Sample dates in range (max. 20) --- + 1: 10-Oct-01 >>>2 === 0 period --now=2010/11/01 2009/10 <<< >>>1 -global details => +--- Period expression tokens --- +TOK_DATE: year 2009 month Oct +END_REACHED: + +--- Before stabilization --- + range: in year 2009 month Oct +--- After stabilization --- + range: in year 2009 month Oct start: 09-Oct-01 finish: 09-Nov-01 - factor: 1 + +--- Sample dates in range (max. 20) --- + 1: 09-Oct-01 >>>2 === 0 period --now=2010/11/01 2009/10/01 <<< >>>1 -global details => +--- Period expression tokens --- +TOK_DATE: year 2009 month Oct day 1 +END_REACHED: + +--- Before stabilization --- + range: in year 2009 month Oct day 1 +--- After stabilization --- + range: in year 2009 month Oct day 1 start: 09-Oct-01 finish: 09-Oct-02 - factor: 1 + +--- Sample dates in range (max. 20) --- + 1: 09-Oct-01 >>>2 === 0 period --now=2010/11/01 2009 <<< >>>1 -global details => +--- Period expression tokens --- +TOK_A_YEAR: 2009 +END_REACHED: +--- Before stabilization --- + range: in year 2009 + +--- After stabilization --- + range: in year 2009 start: 09-Jan-01 finish: 10-Jan-01 - factor: 1 + +--- Sample dates in range (max. 20) --- + 1: 09-Jan-01 >>>2 === 0 diff --git a/test/regress/BBFA1759.test b/test/regress/BBFA1759.test index b109f868..cd5990fc 100644 --- a/test/regress/BBFA1759.test +++ b/test/regress/BBFA1759.test @@ -1,10 +1,20 @@ period june 2008 <<< >>>1 -global details => +--- Period expression tokens --- +TOK_A_MONTH: Jun +TOK_A_YEAR: 2008 +END_REACHED: +--- Before stabilization --- + range: in year 2008 month Jun + +--- After stabilization --- + range: in year 2008 month Jun start: 08-Jun-01 finish: 08-Jul-01 - factor: 1 + +--- Sample dates in range (max. 20) --- + 1: 08-Jun-01 >>>2 === 0 -- cgit v1.2.3 From 93b8f3fe54814850fb2944e7467cd6ccc817b3ba Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 18 Nov 2009 23:14:07 -0500 Subject: Whitespace fix --- src/filters.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/filters.cc') diff --git a/src/filters.cc b/src/filters.cc index 47e7f66d..ff5a9775 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -502,8 +502,7 @@ void changed_value_posts::operator()(post_t& post) bind_scope_t bound_scope(report, post); last_total = total_expr.calc(bound_scope); - - last_post = &post; + last_post = &post; } void subtotal_posts::report_subtotal(const char * spec_fmt, -- cgit v1.2.3 From ae8b57f15785f0fbe300e7d6d2b709b4730556d1 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 18 Nov 2009 23:24:53 -0500 Subject: Renamed bool controlling running total calculations It used to be "account_wise", since it only happens for non-account-wise reports. Now it's called just "calc_running_total", so anyone can request it. --- src/chain.cc | 2 +- src/filters.cc | 4 ++-- src/filters.h | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'src/filters.cc') diff --git a/src/chain.cc b/src/chain.cc index 479f9812..06bcdf27 100644 --- a/src/chain.cc +++ b/src/chain.cc @@ -95,7 +95,7 @@ post_handler_ptr chain_post_handlers(report_t& report, // calc_posts computes the running total. When this appears will determine, // for example, whether filtered posts are included or excluded from the // running total. - handler.reset(new calc_posts(handler, expr)); + handler.reset(new calc_posts(handler, expr, ! for_accounts_report)); // filter_posts will only pass through posts matching the // `secondary_predicate'. diff --git a/src/filters.cc b/src/filters.cc index ff5a9775..4c69dd78 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -217,7 +217,7 @@ void calc_posts::operator()(post_t& post) if (last_post) { assert(last_post->has_xdata()); - if (! account_wise) + if (calc_running_total) xdata.total = last_post->xdata().total; xdata.count = last_post->xdata().count + 1; } else { @@ -230,7 +230,7 @@ void calc_posts::operator()(post_t& post) account_t * acct = post.reported_account(); acct->xdata().add_flags(ACCOUNT_EXT_VISITED); - if (! account_wise) + if (calc_running_total) add_or_set_value(xdata.total, xdata.visited_value); item_handler::operator()(post); diff --git a/src/filters.h b/src/filters.h index 57b3edd2..40119d6d 100644 --- a/src/filters.h +++ b/src/filters.h @@ -280,16 +280,16 @@ class calc_posts : public item_handler { post_t * last_post; expr_t& amount_expr; - bool account_wise; + bool calc_running_total; calc_posts(); public: calc_posts(post_handler_ptr handler, expr_t& _amount_expr, - bool _account_wise = false) + bool _calc_running_total = false) : item_handler(handler), last_post(NULL), - amount_expr(_amount_expr), account_wise(_account_wise) { + amount_expr(_amount_expr), calc_running_total(_calc_running_total) { TRACE_CTOR(calc_posts, "post_handler_ptr, expr_t&, bool"); } virtual ~calc_posts() { -- cgit v1.2.3 From 20965d9fa3c2b355280f25520f0cd74cafcf5fbc Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 19 Nov 2009 00:54:19 -0500 Subject: Changed several pointers to references in filters --- src/chain.cc | 9 +------- src/filters.cc | 67 ++++++++++++++++++++++++++++++++++++---------------------- src/filters.h | 22 +++++-------------- 3 files changed, 48 insertions(+), 50 deletions(-) (limited to 'src/filters.cc') diff --git a/src/chain.cc b/src/chain.cc index 06bcdf27..a3695923 100644 --- a/src/chain.cc +++ b/src/chain.cc @@ -82,14 +82,7 @@ post_handler_ptr chain_post_handlers(report_t& report, // changes in market value of commodities, which otherwise would affect // the running total unpredictably. if (report.HANDLED(revalued)) - handler.reset(new changed_value_posts - (handler, - report.HANDLER(display_amount_).expr, - report.HANDLED(revalued_total_) ? - report.HANDLER(revalued_total_).expr : - report.HANDLER(display_total_).expr, - report.HANDLER(display_total_).expr, - report, report.HANDLED(revalued_only))); + handler.reset(new changed_value_posts(handler, report)); } // calc_posts computes the running total. When this appears will determine, diff --git a/src/filters.cc b/src/filters.cc index 4c69dd78..4ac3de4c 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -239,19 +239,20 @@ void calc_posts::operator()(post_t& post) } namespace { - typedef function post_functor_t; + typedef function post_functor_t; void handle_value(const value_t& value, - account_t * account, + account_t * account, xact_t * xact, temporaries_t& temps, - item_handler& handler, - const date_t& date = date_t(), - const value_t& total = value_t(), + post_handler_ptr handler, + const date_t& date = date_t(), + const value_t& total = value_t(), const bool direct_amount = false, const optional& functor = none) { post_t& post = temps.create_post(*xact, account); + post.add_flags(ITEM_GENERATED); // If the account for this post is all virtual, then report the post as // such. This allows subtotal reports to show "(Account)" for accounts @@ -302,11 +303,11 @@ namespace { xdata.add_flags(POST_EXT_DIRECT_AMT); if (functor) - (*functor)(&post); + (*functor)(post); DEBUG("filter.changed_value.rounding", "post.amount = " << post.amount); - handler(post); + (*handler)(post); } } @@ -344,7 +345,7 @@ void collapse_posts::report_subtotal() earliest_date : last_xact->_date); DEBUG("filter.collapse", "Pseudo-xact date = " << *xact._date); - handle_value(subtotal, &totals_account, &xact, temps, *handler); + handle_value(subtotal, &totals_account, &xact, temps, handler); } component_posts.clear(); @@ -404,30 +405,46 @@ void related_posts::flush() item_handler::flush(); } +changed_value_posts::changed_value_posts(post_handler_ptr handler, + report_t& _report) + : item_handler(handler), report(_report), last_post(NULL), + revalued_account(temps.create_account(_(""))), + rounding_account(temps.create_account(_(""))) +{ + TRACE_CTOR(changed_value_posts, "post_handler_ptr, report_t&, bool"); + + display_amount_expr = report.HANDLER(display_amount_).expr; + total_expr = (report.HANDLED(revalued_total_) ? + report.HANDLER(revalued_total_).expr : + report.HANDLER(display_total_).expr); + display_total_expr = report.HANDLER(display_total_).expr; + changed_values_only = report.HANDLED(revalued_only); +} + void changed_value_posts::flush() { if (last_post && last_post->date() <= report.terminus.date()) { - output_revaluation(last_post, report.terminus.date()); + output_revaluation(*last_post, report.terminus.date()); last_post = NULL; } item_handler::flush(); } -void changed_value_posts::output_revaluation(post_t * post, const date_t& date) +void changed_value_posts::output_revaluation(post_t& post, const date_t& date) { if (is_valid(date)) - post->xdata().date = date; + post.xdata().date = date; value_t repriced_total; try { - bind_scope_t bound_scope(report, *post); + bind_scope_t bound_scope(report, post); repriced_total = total_expr.calc(bound_scope); } catch (...) { - post->xdata().date = date_t(); + post.xdata().date = date_t(); throw; } - post->xdata().date = date_t(); + post.xdata().date = date_t(); DEBUG("filter.changed_value", "output_revaluation(last_balance) = " << last_total); @@ -441,9 +458,9 @@ void changed_value_posts::output_revaluation(post_t * post, const date_t& date) xact_t& xact = temps.create_xact(); xact.payee = _("Commodities revalued"); - xact._date = is_valid(date) ? date : post->date(); + xact._date = is_valid(date) ? date : post.date(); - handle_value(diff, &revalued_account, &xact, temps, *handler, + handle_value(diff, &revalued_account, &xact, temps, handler, *xact._date, repriced_total, false, optional (bind(&changed_value_posts::output_rounding, this, _1))); @@ -451,9 +468,9 @@ void changed_value_posts::output_revaluation(post_t * post, const date_t& date) } } -void changed_value_posts::output_rounding(post_t * post) +void changed_value_posts::output_rounding(post_t& post) { - bind_scope_t bound_scope(report, *post); + bind_scope_t bound_scope(report, post); value_t new_display_total(display_total_expr.calc(bound_scope)); DEBUG("filter.changed_value.rounding", @@ -478,9 +495,9 @@ void changed_value_posts::output_rounding(post_t * post) xact_t& xact = temps.create_xact(); xact.payee = _("Commodity rounding"); - xact._date = post->date(); + xact._date = post.date(); - handle_value(diff, &rounding_account, &xact, temps, *handler, + handle_value(diff, &rounding_account, &xact, temps, handler, *xact._date, precise_display_total, true); } } @@ -491,12 +508,12 @@ void changed_value_posts::output_rounding(post_t * post) void changed_value_posts::operator()(post_t& post) { if (last_post) - output_revaluation(last_post, post.date()); + output_revaluation(*last_post, post.date()); if (changed_values_only) post.xdata().add_flags(POST_EXT_DISPLAYED); - output_rounding(&post); + output_rounding(post); item_handler::operator()(post); @@ -543,7 +560,7 @@ void subtotal_posts::report_subtotal(const char * spec_fmt, foreach (values_map::value_type& pair, values) handle_value(pair.second.value, pair.second.account, &xact, temps, - *handler); + handler); values.clear(); } @@ -653,10 +670,10 @@ void posts_as_equity::report_subtotal() foreach (balance_t::amounts_map::value_type amount_pair, pair.second.value.as_balance().amounts) handle_value(amount_pair.second, pair.second.account, &xact, temps, - *handler); + handler); } else { handle_value(pair.second.value, pair.second.account, &xact, temps, - *handler); + handler); } total += pair.second.value; } diff --git a/src/filters.h b/src/filters.h index 40119d6d..2222878e 100644 --- a/src/filters.h +++ b/src/filters.h @@ -382,7 +382,7 @@ class changed_value_posts : public item_handler post_t * last_post; value_t last_total; value_t last_display_total; - temporaries_t temps; + temporaries_t temps; account_t& revalued_account; account_t& rounding_account; @@ -390,28 +390,16 @@ class changed_value_posts : public item_handler public: changed_value_posts(post_handler_ptr handler, - const expr_t& _display_amount_expr, - const expr_t& _total_expr, - const expr_t& _display_total_expr, - report_t& _report, - bool _changed_values_only) - : item_handler(handler), - display_amount_expr(_display_amount_expr), total_expr(_total_expr), - display_total_expr(_display_total_expr), report(_report), - changed_values_only(_changed_values_only), last_post(NULL), - revalued_account(temps.create_account(_(""))), - rounding_account(temps.create_account(_(""))) { - TRACE_CTOR(changed_value_posts, - "post_handler_ptr, const expr_t&, const expr_t&, report_t&, bool"); - } + report_t& _report); + virtual ~changed_value_posts() { TRACE_DTOR(changed_value_posts); } virtual void flush(); - void output_revaluation(post_t * post, const date_t& current); - void output_rounding(post_t * post); + void output_revaluation(post_t& post, const date_t& current); + void output_rounding(post_t& post); virtual void operator()(post_t& post); }; -- cgit v1.2.3 From 63fee4c83775f79364199ea547dbc7e068b0abc8 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 19 Nov 2009 02:00:10 -0500 Subject: Added an --unrealized option, for use with bal -V When this option is on, then in balance report which show market values, any gains or losses in value will be balanced into a pair of accounts called Equity:Unrealized Gains and Equity:Unrealized Losses. --- src/chain.cc | 19 ++++++++----- src/filters.cc | 58 ++++++++++++++++++++++++++++++++++----- src/filters.h | 8 +++++- src/report.cc | 1 + src/report.h | 3 ++ test/baseline/opt-unrealized.test | 20 ++++++++++++++ 6 files changed, 94 insertions(+), 15 deletions(-) create mode 100644 test/baseline/opt-unrealized.test (limited to 'src/filters.cc') diff --git a/src/chain.cc b/src/chain.cc index 9ab26bd6..113a71d8 100644 --- a/src/chain.cc +++ b/src/chain.cc @@ -77,18 +77,23 @@ post_handler_ptr chain_post_handlers(report_t& report, report.what_to_keep()); handler.reset(new filter_posts(handler, display_predicate, report)); } - - // changed_value_posts adds virtual posts to the list to account for - // changes in market value of commodities, which otherwise would affect - // the running total unpredictably. - if (report.HANDLED(revalued)) - handler.reset(new changed_value_posts(handler, report)); } + // changed_value_posts adds virtual posts to the list to account for changes + // in market value of commodities, which otherwise would affect the running + // total unpredictably. + if (report.HANDLED(revalued) && (! for_accounts_report || + report.HANDLED(unrealized))) + handler.reset(new changed_value_posts(handler, report, + for_accounts_report, + report.HANDLED(unrealized))); + // calc_posts computes the running total. When this appears will determine, // for example, whether filtered posts are included or excluded from the // running total. - handler.reset(new calc_posts(handler, expr, ! for_accounts_report)); + handler.reset(new calc_posts(handler, expr, (! for_accounts_report || + (report.HANDLED(revalued) && + report.HANDLED(unrealized))))); // filter_posts will only pass through posts matching the // `secondary_predicate'. diff --git a/src/filters.cc b/src/filters.cc index 4ac3de4c..39097c58 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -249,6 +249,7 @@ namespace { const date_t& date = date_t(), const value_t& total = value_t(), const bool direct_amount = false, + const bool mark_visited = false, const optional& functor = none) { post_t& post = temps.create_post(*xact, account); @@ -308,6 +309,11 @@ namespace { DEBUG("filter.changed_value.rounding", "post.amount = " << post.amount); (*handler)(post); + + if (mark_visited) { + post.xdata().add_flags(POST_EXT_VISITED); + post.account->xdata().add_flags(ACCOUNT_EXT_VISITED); + } } } @@ -406,8 +412,12 @@ void related_posts::flush() } changed_value_posts::changed_value_posts(post_handler_ptr handler, - report_t& _report) - : item_handler(handler), report(_report), last_post(NULL), + report_t& _report, + bool _for_accounts_report, + bool _show_unrealized) + : item_handler(handler), report(_report), + for_accounts_report(_for_accounts_report), + show_unrealized(_show_unrealized), last_post(NULL), revalued_account(temps.create_account(_(""))), rounding_account(temps.create_account(_(""))) { @@ -419,6 +429,14 @@ changed_value_posts::changed_value_posts(post_handler_ptr handler, report.HANDLER(display_total_).expr); display_total_expr = report.HANDLER(display_total_).expr; changed_values_only = report.HANDLED(revalued_only); + + gains_equity_account = + report.session.journal->master->find_account(_("Equity:Unrealized Gains")); + gains_equity_account->add_flags(ACCOUNT_GENERATED); + + losses_equity_account = + report.session.journal->master->find_account(_("Equity:Unrealized Losses")); + losses_equity_account->add_flags(ACCOUNT_GENERATED); } void changed_value_posts::flush() @@ -460,10 +478,35 @@ void changed_value_posts::output_revaluation(post_t& post, const date_t& date) xact.payee = _("Commodities revalued"); xact._date = is_valid(date) ? date : post.date(); - handle_value(diff, &revalued_account, &xact, temps, handler, - *xact._date, repriced_total, false, - optional - (bind(&changed_value_posts::output_rounding, this, _1))); + if (! for_accounts_report) { + handle_value + (/* value= */ diff, + /* account= */ &revalued_account, + /* xact= */ &xact, + /* temps= */ temps, + /* handler= */ handler, + /* date= */ *xact._date, + /* total= */ repriced_total, + /* direct_amount= */ false, + /* mark_visited= */ false, + /* functor= */ (optional + (bind(&changed_value_posts::output_rounding, + this, _1)))); + } + else if (show_unrealized) { + handle_value + (/* value= */ - diff, + /* account= */ (diff < 0L ? + losses_equity_account : + gains_equity_account), + /* xact= */ &xact, + /* temps= */ temps, + /* handler= */ handler, + /* date= */ *xact._date, + /* total= */ value_t(), + /* direct_amount= */ false, + /* mark_visited= */ true); + } } } } @@ -513,7 +556,8 @@ void changed_value_posts::operator()(post_t& post) if (changed_values_only) post.xdata().add_flags(POST_EXT_DISPLAYED); - output_rounding(post); + if (! for_accounts_report) + output_rounding(post); item_handler::operator()(post); diff --git a/src/filters.h b/src/filters.h index f9412407..92148dbe 100644 --- a/src/filters.h +++ b/src/filters.h @@ -378,18 +378,24 @@ class changed_value_posts : public item_handler expr_t display_total_expr; report_t& report; bool changed_values_only; + bool for_accounts_report; + bool show_unrealized; post_t * last_post; value_t last_total; value_t last_display_total; temporaries_t temps; account_t& revalued_account; account_t& rounding_account; + account_t * gains_equity_account; + account_t * losses_equity_account; changed_value_posts(); public: changed_value_posts(post_handler_ptr handler, - report_t& _report); + report_t& _report, + bool _for_accounts_report, + bool _show_unrealized); virtual ~changed_value_posts() { TRACE_DTOR(changed_value_posts); diff --git a/src/report.cc b/src/report.cc index 92525565..7da44f8c 100644 --- a/src/report.cc +++ b/src/report.cc @@ -887,6 +887,7 @@ option_t * report_t::lookup_option(const char * p) case 'u': OPT(unbudgeted); else OPT(uncleared); + else OPT(unrealized); else OPT(unround); else OPT(unsorted); break; diff --git a/src/report.h b/src/report.h index 0b1baff1..354e31b6 100644 --- a/src/report.h +++ b/src/report.h @@ -301,6 +301,7 @@ public: HANDLER(truncate_).report(out); HANDLER(unbudgeted).report(out); HANDLER(uncleared).report(out); + HANDLER(unrealized).report(out); HANDLER(unround).report(out); HANDLER(unsorted).report(out); HANDLER(weekly).report(out); @@ -873,6 +874,8 @@ public: parent->HANDLER(limit_).on(string("--uncleared"), "uncleared|pending"); }); + OPTION(report_t, unrealized); + OPTION_(report_t, unround, DO() { parent->HANDLER(display_amount_) .set_expr(string("--unround"), "unrounded(amount_expr)"); diff --git a/test/baseline/opt-unrealized.test b/test/baseline/opt-unrealized.test new file mode 100644 index 00000000..7d5d20fb --- /dev/null +++ b/test/baseline/opt-unrealized.test @@ -0,0 +1,20 @@ +bal -V --unrealized +<<< +2008/10/01 Sample + Assets:Brokerage 10 AAPL + Assets:Checking $-200.00 + +P 2008/10/20 12:00:00 AAPL $30.00 + +; 2008/10/20 +; Assets:Brokerage $100 +; Equity:Unrealized Gains +>>>1 + $100.00 Assets + $300.00 Brokerage + $-200.00 Checking + $-100.00 Equity:Unrealized Gains +-------------------- + 0 +>>>2 +=== 0 -- cgit v1.2.3