diff options
author | John Wiegley <johnw@newartisans.com> | 2010-06-06 06:20:07 -0400 |
---|---|---|
committer | John Wiegley <johnw@newartisans.com> | 2010-06-06 06:20:07 -0400 |
commit | 39f9854e2c1f807f6e9c90d80e1eec2bf9b90017 (patch) | |
tree | b72e9a1454b6721d59e3844d71a91880c94e2cae | |
parent | 4197c8851120e5c5ac7426cdca209a49049e9363 (diff) | |
download | fork-ledger-39f9854e2c1f807f6e9c90d80e1eec2bf9b90017.tar.gz fork-ledger-39f9854e2c1f807f6e9c90d80e1eec2bf9b90017.tar.bz2 fork-ledger-39f9854e2c1f807f6e9c90d80e1eec2bf9b90017.zip |
Reworked the way that <Rounding> entries are shown
Fixes #188 / 53BCED29-F3B9-4E02-9A35-6C739ABB9662
-rw-r--r-- | src/chain.cc | 34 | ||||
-rw-r--r-- | src/chain.h | 16 | ||||
-rw-r--r-- | src/filters.cc | 156 | ||||
-rw-r--r-- | src/filters.h | 71 | ||||
-rw-r--r-- | src/py_journal.cc | 4 | ||||
-rw-r--r-- | src/report.cc | 50 | ||||
-rw-r--r-- | test/baseline/opt-gain.test | 1 | ||||
-rw-r--r-- | test/baseline/opt-price.test | 2 |
8 files changed, 202 insertions, 132 deletions
diff --git a/src/chain.cc b/src/chain.cc index b8c2eb0a..86d35f14 100644 --- a/src/chain.cc +++ b/src/chain.cc @@ -39,8 +39,8 @@ namespace ledger { -post_handler_ptr chain_pre_post_handlers(report_t& report, - post_handler_ptr base_handler) +post_handler_ptr chain_pre_post_handlers(post_handler_ptr base_handler, + report_t& report) { post_handler_ptr handler(base_handler); @@ -106,13 +106,14 @@ post_handler_ptr chain_pre_post_handlers(report_t& report, return handler; } -post_handler_ptr chain_post_handlers(report_t& report, - post_handler_ptr base_handler, +post_handler_ptr chain_post_handlers(post_handler_ptr base_handler, + report_t& report, bool for_accounts_report) { - post_handler_ptr handler(base_handler); - predicate_t display_predicate; - predicate_t only_predicate; + post_handler_ptr handler(base_handler); + predicate_t display_predicate; + predicate_t only_predicate; + rounding_error_posts * rounding_handler = NULL; assert(report.HANDLED(amount_)); expr_t& expr(report.HANDLER(amount_).expr); @@ -137,6 +138,14 @@ post_handler_ptr chain_post_handlers(report_t& report, report.HANDLED(tail_) ? report.HANDLER(tail_).value.to_int() : 0)); + // 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) && ! report.HANDLED(no_rounding)) { + rounding_handler = new rounding_error_posts(handler, report); + handler.reset(rounding_handler); + } + // filter_posts will only pass through posts matching the // `display_predicate'. if (report.HANDLED(display_)) { @@ -149,12 +158,11 @@ post_handler_ptr chain_post_handlers(report_t& 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, + if (report.HANDLED(revalued) && + (! for_accounts_report || report.HANDLED(unrealized))) + handler.reset(new changed_value_posts(handler, report, for_accounts_report, report.HANDLED(unrealized), - ! report.HANDLED(no_rounding))); + rounding_handler)); // calc_posts computes the running total. When this appears will determine, // for example, whether filtered posts are included or excluded from the @@ -190,7 +198,7 @@ post_handler_ptr chain_post_handlers(report_t& report, // collapse_posts causes xacts with multiple posts to appear as xacts // with a subtotaled post for each commodity used. if (report.HANDLED(collapse)) - handler.reset(new collapse_posts(handler, expr, + handler.reset(new collapse_posts(handler, report, expr, display_predicate, only_predicate, report.HANDLED(collapse_if_zero))); diff --git a/src/chain.h b/src/chain.h index 59b04eb8..1a50a077 100644 --- a/src/chain.h +++ b/src/chain.h @@ -93,20 +93,20 @@ typedef shared_ptr<item_handler<account_t> > acct_handler_ptr; class report_t; post_handler_ptr -chain_pre_post_handlers(report_t& report, - post_handler_ptr base_handler); +chain_pre_post_handlers(post_handler_ptr base_handler, + report_t& report); post_handler_ptr -chain_post_handlers(report_t& report, - post_handler_ptr base_handler, +chain_post_handlers(post_handler_ptr base_handler, + report_t& report, bool for_accounts_report = false); inline post_handler_ptr -chain_handlers(report_t& report, - post_handler_ptr handler, +chain_handlers(post_handler_ptr handler, + report_t& report, bool for_accounts_report = false) { - handler = chain_post_handlers(report, handler, for_accounts_report); - handler = chain_pre_post_handlers(report, handler); + handler = chain_post_handlers(handler, report, for_accounts_report); + handler = chain_pre_post_handlers(handler, report); return handler; } diff --git a/src/filters.cc b/src/filters.cc index 07278500..e5c5275c 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -374,7 +374,8 @@ void collapse_posts::report_subtotal() std::size_t displayed_count = 0; foreach (post_t * post, component_posts) { - if (only_predicate(*post) && display_predicate(*post)) + bind_scope_t bound_scope(report, *post); + if (only_predicate(bound_scope) && display_predicate(bound_scope)) displayed_count++; } @@ -401,7 +402,11 @@ void collapse_posts::report_subtotal() earliest_date : last_xact->_date); DEBUG("filters.collapse", "Pseudo-xact date = " << *xact._date); - handle_value(subtotal, &totals_account, &xact, temps, handler); + handle_value(/* value= */ subtotal, + /* account= */ &totals_account, + /* xact= */ &xact, + /* temps= */ temps, + /* handler= */ handler); } component_posts.clear(); @@ -461,21 +466,81 @@ void related_posts::flush() item_handler<post_t>::flush(); } -changed_value_posts::changed_value_posts(post_handler_ptr handler, - report_t& _report, - bool _for_accounts_report, - bool _show_unrealized, - bool _show_rounding) +rounding_error_posts::rounding_error_posts(post_handler_ptr handler, + report_t& _report) + : item_handler<post_t>(handler), report(_report), + rounding_account(temps.create_account(_("<Rounding>"))) +{ + TRACE_CTOR(rounding_error_posts, "post_handler_ptr, report_t&"); + + display_amount_expr = report.HANDLER(display_amount_).expr; + display_total_expr = report.HANDLER(display_total_).expr; +} + +void rounding_error_posts::output_rounding(post_t& post) +{ + bind_scope_t bound_scope(report, post); + value_t new_display_total(display_total_expr.calc(bound_scope)); + + DEBUG("filters.changed_value.rounding", + "rounding.new_display_total = " << new_display_total); + + if (! last_display_total.is_null()) { + if (value_t repriced_amount = display_amount_expr.calc(bound_scope)) { + DEBUG("filters.changed_value.rounding", + "rounding.repriced_amount = " << repriced_amount); + + value_t precise_display_total(new_display_total.truncated() - + repriced_amount.truncated()); + + DEBUG("filters.changed_value.rounding", + "rounding.precise_display_total = " << precise_display_total); + DEBUG("filters.changed_value.rounding", + "rounding.last_display_total = " << last_display_total); + + if (value_t diff = precise_display_total - last_display_total) { + DEBUG("filters.changed_value.rounding", + "rounding.diff = " << diff); + + xact_t& xact = temps.create_xact(); + xact.payee = _("Commodity rounding"); + xact._date = post.date(); + + handle_value(/* value= */ diff, + /* account= */ &rounding_account, + /* xact= */ &xact, + /* temps= */ temps, + /* handler= */ handler, + /* date= */ *xact._date, + /* total= */ precise_display_total, + /* direct_amount= */ true); + } + } + } + last_display_total = new_display_total; +} + +void rounding_error_posts::operator()(post_t& post) +{ + output_rounding(post); + + item_handler<post_t>::operator()(post); +} + +changed_value_posts::changed_value_posts + (post_handler_ptr handler, + report_t& _report, + bool _for_accounts_report, + bool _show_unrealized, + rounding_error_posts * _rounding_handler) : item_handler<post_t>(handler), report(_report), for_accounts_report(_for_accounts_report), - show_unrealized(_show_unrealized), - show_rounding(_show_rounding), last_post(NULL), + show_unrealized(_show_unrealized), last_post(NULL), revalued_account(temps.create_account(_("<Revalued>"))), - rounding_account(temps.create_account(_("<Rounding>"))) + rounding_handler(_rounding_handler) { 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); @@ -549,13 +614,7 @@ void changed_value_posts::output_revaluation(post_t& post, const date_t& date) /* temps= */ temps, /* handler= */ handler, /* date= */ *xact._date, - /* total= */ repriced_total, - /* direct_amount= */ false, - /* mark_visited= */ false, - /* functor= */ (show_rounding ? - optional<post_functor_t> - (bind(&changed_value_posts::output_rounding, - this, _1)) : none)); + /* total= */ repriced_total); } else if (show_unrealized) { handle_value @@ -692,43 +751,6 @@ void changed_value_posts::output_intermediate_prices(post_t& post, } } -void changed_value_posts::output_rounding(post_t& post) -{ - bind_scope_t bound_scope(report, post); - value_t new_display_total(display_total_expr.calc(bound_scope)); - - DEBUG("filters.changed_value.rounding", - "rounding.new_display_total = " << new_display_total); - - if (! last_display_total.is_null()) { - if (value_t repriced_amount = display_amount_expr.calc(bound_scope)) { - DEBUG("filters.changed_value.rounding", - "rounding.repriced_amount = " << repriced_amount); - - value_t precise_display_total(new_display_total.truncated() - - repriced_amount.truncated()); - - DEBUG("filters.changed_value.rounding", - "rounding.precise_display_total = " << precise_display_total); - DEBUG("filters.changed_value.rounding", - "rounding.last_display_total = " << last_display_total); - - if (value_t diff = precise_display_total - last_display_total) { - DEBUG("filters.changed_value.rounding", - "rounding.diff = " << diff); - - xact_t& xact = temps.create_xact(); - xact.payee = _("Commodity rounding"); - xact._date = post.date(); - - handle_value(diff, &rounding_account, &xact, temps, handler, - *xact._date, precise_display_total, true); - } - } - } - last_display_total = new_display_total; -} - void changed_value_posts::operator()(post_t& post) { if (last_post) { @@ -740,9 +762,6 @@ void changed_value_posts::operator()(post_t& post) if (changed_values_only) post.xdata().add_flags(POST_EXT_DISPLAYED); - if (! for_accounts_report && show_rounding) - output_rounding(post); - item_handler<post_t>::operator()(post); bind_scope_t bound_scope(report, post); @@ -787,8 +806,11 @@ void subtotal_posts::report_subtotal(const char * spec_fmt, xact._date = *range_start; foreach (values_map::value_type& pair, values) - handle_value(pair.second.value, pair.second.account, &xact, temps, - handler); + handle_value(/* value= */ pair.second.value, + /* account= */ pair.second.account, + /* xact= */ &xact, + /* temps= */ temps, + /* handler= */ handler); values.clear(); } @@ -897,11 +919,17 @@ void posts_as_equity::report_subtotal() if (pair.second.value.is_balance()) { foreach (const 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); + handle_value(/* value= */ amount_pair.second, + /* account= */ pair.second.account, + /* xact= */ &xact, + /* temps= */ temps, + /* handler= */ handler); } else { - handle_value(pair.second.value, pair.second.account, &xact, temps, - handler); + handle_value(/* value= */ pair.second.value, + /* account= */ pair.second.account, + /* xact= */ &xact, + /* temps= */ temps, + /* handler= */ handler); } total += pair.second.value; } diff --git a/src/filters.h b/src/filters.h index 327499fb..22b27c5d 100644 --- a/src/filters.h +++ b/src/filters.h @@ -63,17 +63,17 @@ public: protected: value_to_posts_map posts_map; - report_t& report; post_handler_ptr post_chain; + report_t& report; expr_t group_by_expr; custom_flusher_t preflush_func; optional<custom_flusher_t> postflush_func; public: - post_splitter(report_t& _report, - post_handler_ptr _post_chain, + post_splitter(post_handler_ptr _post_chain, + report_t& _report, expr_t _group_by_expr) - : report(_report), post_chain(_post_chain), + : post_chain(_post_chain), report(_report), group_by_expr(_group_by_expr), preflush_func(bind(&post_splitter::print_title, this, _1)) { TRACE_CTOR(post_splitter, "scope_t&, post_handler_ptr, expr_t"); @@ -401,11 +401,13 @@ class collapse_posts : public item_handler<post_t> account_t& totals_account; bool only_collapse_if_zero; std::list<post_t *> component_posts; + report_t& report; collapse_posts(); public: collapse_posts(post_handler_ptr handler, + report_t& _report, expr_t& _amount_expr, predicate_t _display_predicate, predicate_t _only_predicate, @@ -415,8 +417,8 @@ public: only_predicate(_only_predicate), count(0), last_xact(NULL), last_post(NULL), totals_account(temps.create_account(_("<Total>"))), - only_collapse_if_zero(_only_collapse_if_zero) { - TRACE_CTOR(collapse_posts, "post_handler_ptr"); + only_collapse_if_zero(_only_collapse_if_zero), report(_report) { + TRACE_CTOR(collapse_posts, "post_handler_ptr, ..."); } virtual ~collapse_posts() { TRACE_DTOR(collapse_posts); @@ -479,37 +481,73 @@ public: } }; -class changed_value_posts : public item_handler<post_t> +class rounding_error_posts : public item_handler<post_t> { // This filter requires that calc_posts be used at some point // later in the chain. expr_t display_amount_expr; + expr_t display_total_expr; + report_t& report; + value_t last_display_total; + temporaries_t temps; + account_t& rounding_account; + + rounding_error_posts(); + +public: + rounding_error_posts(post_handler_ptr handler, + report_t& _report); + + virtual ~rounding_error_posts() { + TRACE_DTOR(rounding_error_posts); + } + + void output_rounding(post_t& post); + + virtual void operator()(post_t& post); + + virtual void clear() { + display_amount_expr.mark_uncompiled(); + display_total_expr.mark_uncompiled(); + + last_display_total = value_t(); + + temps.clear(); + + item_handler<post_t>::clear(); + } +}; + +class changed_value_posts : public item_handler<post_t> +{ + // This filter requires that calc_posts be used at some point + // later in the chain. + expr_t total_expr; expr_t display_total_expr; report_t& report; bool changed_values_only; bool for_accounts_report; bool show_unrealized; - bool show_rounding; post_t * last_post; value_t last_total; - value_t last_display_total; value_t repriced_total; temporaries_t temps; account_t& revalued_account; - account_t& rounding_account; account_t * gains_equity_account; account_t * losses_equity_account; + rounding_error_posts * rounding_handler; + changed_value_posts(); public: - changed_value_posts(post_handler_ptr handler, - report_t& _report, - bool _for_accounts_report, - bool _show_unrealized, - bool _show_rounding); + changed_value_posts(post_handler_ptr handler, + report_t& _report, + bool _for_accounts_report, + bool _show_unrealized, + rounding_error_posts * _rounding_handler); virtual ~changed_value_posts() { TRACE_DTOR(changed_value_posts); @@ -519,18 +557,15 @@ public: void output_revaluation(post_t& post, const date_t& current); void output_intermediate_prices(post_t& post, const date_t& current); - void output_rounding(post_t& post); virtual void operator()(post_t& post); virtual void clear() { - display_amount_expr.mark_uncompiled(); total_expr.mark_uncompiled(); display_total_expr.mark_uncompiled(); last_post = NULL; last_total = value_t(); - last_display_total = value_t(); temps.clear(); diff --git a/src/py_journal.cc b/src/py_journal.cc index 1848adc4..cd25d134 100644 --- a/src/py_journal.cc +++ b/src/py_journal.cc @@ -190,8 +190,8 @@ namespace { journal_posts_iterator walker(coll->journal); coll->chain = - chain_post_handlers(coll->report, - post_handler_ptr(coll->posts_collector)); + chain_post_handlers(post_handler_ptr(coll->posts_collector), + coll->report); pass_down_posts(coll->chain, walker); } catch (...) { diff --git a/src/report.cc b/src/report.cc index f0923b33..4302187e 100644 --- a/src/report.cc +++ b/src/report.cc @@ -141,7 +141,9 @@ void report_t::normalize_options(const string& verb) HANDLER(limit_).on(string("?normalize"), "actual"); if (! HANDLED(empty)) - HANDLER(display_).on(string("?normalize"), "amount|(!post&total)"); + HANDLER(display_).on(string("?normalize"), + string("(post?(display_amount|account=\"") + + _("<Revalued>") + "\"):display_total)"); if (verb[0] != 'b' && verb[0] != 'r') HANDLER(base).on_only(string("?normalize")); @@ -284,11 +286,11 @@ void report_t::parse_query_args(const value_t& args, const string& whence) namespace { struct posts_flusher { - report_t& report; post_handler_ptr handler; + report_t& report; - posts_flusher(report_t& _report, post_handler_ptr _handler) - : report(_report), handler(_handler) {} + posts_flusher(post_handler_ptr _handler, report_t& _report) + : handler(_handler), report(_report) {} void operator()(const value_t&) { report.session.journal->clear_xdata(); @@ -298,27 +300,27 @@ namespace { void report_t::posts_report(post_handler_ptr handler) { - handler = chain_post_handlers(*this, handler); + handler = chain_post_handlers(handler, *this); if (HANDLED(group_by_)) { std::auto_ptr<post_splitter> - splitter(new post_splitter(*this, handler, HANDLER(group_by_).expr)); - splitter->set_postflush_func(posts_flusher(*this, handler)); + splitter(new post_splitter(handler, *this, HANDLER(group_by_).expr)); + splitter->set_postflush_func(posts_flusher(handler, *this)); handler = post_handler_ptr(splitter.release()); } - handler = chain_pre_post_handlers(*this, handler); + handler = chain_pre_post_handlers(handler, *this); journal_posts_iterator walker(*session.journal.get()); pass_down_posts(handler, walker); if (! HANDLED(group_by_)) - posts_flusher(*this, handler)(value_t()); + posts_flusher(handler, *this)(value_t()); } void report_t::generate_report(post_handler_ptr handler) { HANDLER(limit_).on(string("#generate"), "actual"); - handler = chain_handlers(*this, handler); + handler = chain_handlers(handler, *this); generate_posts_iterator walker (session, HANDLED(seed_) ? @@ -331,7 +333,7 @@ void report_t::generate_report(post_handler_ptr handler) void report_t::xact_report(post_handler_ptr handler, xact_t& xact) { - handler = chain_handlers(*this, handler); + handler = chain_handlers(handler, *this); xact_posts_iterator walker(xact); pass_down_posts(handler, walker); @@ -342,11 +344,11 @@ void report_t::xact_report(post_handler_ptr handler, xact_t& xact) namespace { struct accounts_title_printer { - report_t& report; acct_handler_ptr handler; + report_t& report; - accounts_title_printer(report_t& _report, acct_handler_ptr _handler) - : report(_report), handler(_handler) {} + accounts_title_printer(acct_handler_ptr _handler, report_t& _report) + : handler(_handler), report(_report) {} void operator()(const value_t& val) { @@ -360,11 +362,11 @@ namespace { struct accounts_flusher { - report_t& report; acct_handler_ptr handler; + report_t& report; - accounts_flusher(report_t& _report, acct_handler_ptr _handler) - : report(_report), handler(_handler) {} + accounts_flusher(acct_handler_ptr _handler, report_t& _report) + : handler(_handler), report(_report) {} void operator()(const value_t&) { @@ -403,18 +405,18 @@ namespace { void report_t::accounts_report(acct_handler_ptr handler) { post_handler_ptr chain = - chain_post_handlers(*this, post_handler_ptr(new ignore_posts), + chain_post_handlers(post_handler_ptr(new ignore_posts), *this, /* for_accounts_report= */ true); if (HANDLED(group_by_)) { std::auto_ptr<post_splitter> - splitter(new post_splitter(*this, chain, HANDLER(group_by_).expr)); + splitter(new post_splitter(chain, *this, HANDLER(group_by_).expr)); - splitter->set_preflush_func(accounts_title_printer(*this, handler)); - splitter->set_postflush_func(accounts_flusher(*this, handler)); + splitter->set_preflush_func(accounts_title_printer(handler, *this)); + splitter->set_postflush_func(accounts_flusher(handler, *this)); chain = post_handler_ptr(splitter.release()); } - chain = chain_pre_post_handlers(*this, chain); + chain = chain_pre_post_handlers(chain, *this); // The lifetime of the chain object controls the lifetime of all temporary // objects created within it during the call to pass_down_posts, which will @@ -423,12 +425,12 @@ void report_t::accounts_report(acct_handler_ptr handler) pass_down_posts(chain, walker); if (! HANDLED(group_by_)) - accounts_flusher(*this, handler)(value_t()); + accounts_flusher(handler, *this)(value_t()); } void report_t::commodities_report(post_handler_ptr handler) { - handler = chain_handlers(*this, handler); + handler = chain_handlers(handler, *this); posts_commodities_iterator walker(*session.journal.get()); pass_down_posts(handler, walker); diff --git a/test/baseline/opt-gain.test b/test/baseline/opt-gain.test index 63055d5f..6d139c79 100644 --- a/test/baseline/opt-gain.test +++ b/test/baseline/opt-gain.test @@ -49,7 +49,6 @@ P 2010/03/01 00:00:00 S 8 P P 2010/04/01 00:00:00 S 16 P >>>1 -09-Jan-01 Sample 1a As:Brokerage:Stocks 0 0 09-Jan-15 Commodities revalued <Revalued> 100 P 100 P 09-Feb-01 Commodities revalued <Revalued> 200 P 300 P 09-Feb-01 Sample 2a As:Brokerage:Stocks 300 P 600 P diff --git a/test/baseline/opt-price.test b/test/baseline/opt-price.test index 133b2155..67f8161b 100644 --- a/test/baseline/opt-price.test +++ b/test/baseline/opt-price.test @@ -34,9 +34,7 @@ reg --end 2009/06/26 -V equities === 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-Jan-31 Commodities revalued <Revalued> $250 $750 09-Jun-26 Commodities revalued <Revalued> $500 $1250 >>>2 |