Reworked the way that <Rounding> entries are shown

Fixes #188 / 53BCED29-F3B9-4E02-9A35-6C739ABB9662
This commit is contained in:
John Wiegley 2010-06-06 06:20:07 -04:00
parent 4197c88511
commit 39f9854e2c
8 changed files with 202 additions and 132 deletions

View file

@ -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)));

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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();

View file

@ -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 (...) {

View file

@ -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);

View file

@ -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

View file

@ -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