Rewrote the equity command, which is working again

The old implementation used an account formatter, and was very
specialized.  The new is done as a transaction filter, and works along
with everything else, eliminating bugs special to the equity report.
This commit is contained in:
John Wiegley 2009-02-20 02:53:54 -04:00
parent f2f52066d2
commit c1b25fcf86
8 changed files with 110 additions and 140 deletions

View file

@ -113,7 +113,9 @@ xact_handler_ptr chain_xact_handlers(report_t& report,
// //
// dow_xacts is like period_xacts, except that it reports all the xacts // dow_xacts is like period_xacts, except that it reports all the xacts
// that fall on each subsequent day of the week. // that fall on each subsequent day of the week.
if (report.HANDLED(subtotal)) if (report.HANDLED(equity))
handler.reset(new xacts_as_equity(handler, expr));
else if (report.HANDLED(subtotal))
handler.reset(new subtotal_xacts(handler, expr)); handler.reset(new subtotal_xacts(handler, expr));
} }

View file

@ -516,7 +516,7 @@ void interval_xacts::operator()(xact_t& xact)
xact_temps.push_back(xact_t(&empty_account)); xact_temps.push_back(xact_t(&empty_account));
xact_t& null_xact = xact_temps.back(); xact_t& null_xact = xact_temps.back();
null_xact.add_flags(ITEM_TEMP); null_xact.add_flags(ITEM_TEMP | XACT_CALCULATED);
null_xact.amount = 0L; null_xact.amount = 0L;
null_entry.add_xact(&null_xact); null_entry.add_xact(&null_xact);
@ -536,6 +536,49 @@ void interval_xacts::operator()(xact_t& xact)
last_xact = &xact; last_xact = &xact;
} }
void xacts_as_equity::report_subtotal()
{
date_t finish;
foreach (xact_t * xact, component_xacts) {
date_t date = xact->reported_date();
if (! is_valid(finish) || date > finish)
finish = date;
}
component_xacts.clear();
entry_temps.push_back(entry_t());
entry_t& entry = entry_temps.back();
entry.payee = "Opening Balances";
entry._date = finish;
value_t total = 0L;
foreach (values_map::value_type& pair, values) {
handle_value(pair.second.value, pair.second.account, &entry, 0,
xact_temps, *handler);
total += pair.second.value;
}
values.clear();
if (total.is_balance()) {
foreach (balance_t::amounts_map::value_type pair,
total.as_balance().amounts) {
xact_temps.push_back(xact_t(balance_account));
xact_t& balance_xact = xact_temps.back();
balance_xact.add_flags(ITEM_TEMP);
balance_xact.amount = - pair.second;
entry.add_xact(&balance_xact);
(*handler)(balance_xact);
}
} else {
xact_temps.push_back(xact_t(balance_account));
xact_t& balance_xact = xact_temps.back();
balance_xact.add_flags(ITEM_TEMP);
balance_xact.amount = - total.to_amount();
entry.add_xact(&balance_xact);
(*handler)(balance_xact);
}
}
by_payee_xacts::~by_payee_xacts() by_payee_xacts::~by_payee_xacts()
{ {
TRACE_DTOR(by_payee_xacts); TRACE_DTOR(by_payee_xacts);

View file

@ -611,6 +611,34 @@ public:
virtual void operator()(xact_t& xact); virtual void operator()(xact_t& xact);
}; };
class xacts_as_equity : public subtotal_xacts
{
interval_t interval;
xact_t * last_xact;
account_t equity_account;
account_t * balance_account;
xacts_as_equity();
public:
xacts_as_equity(xact_handler_ptr _handler, expr_t& amount_expr)
: subtotal_xacts(_handler, amount_expr),
equity_account(NULL, "Equity") {
TRACE_CTOR(xacts_as_equity, "xact_handler_ptr, expr_t&");
balance_account = equity_account.find_account("Opening Balances");
}
virtual ~xacts_as_equity() throw() {
TRACE_DTOR(xacts_as_equity);
}
void report_subtotal();
virtual void flush() {
report_subtotal();
subtotal_xacts::flush();
}
};
/** /**
* @brief Brief * @brief Brief
* *

View file

@ -339,7 +339,7 @@ void global_scope_t::normalize_session_options()
function_t global_scope_t::look_for_precommand(scope_t& scope, function_t global_scope_t::look_for_precommand(scope_t& scope,
const string& verb) const string& verb)
{ {
if (expr_t::ptr_op_t def = scope.lookup(string("precmd_") + verb)) if (expr_t::ptr_op_t def = scope.lookup(string(PRECMD_PREFIX) + verb))
return def->as_function(); return def->as_function();
else else
return function_t(); return function_t();
@ -348,7 +348,7 @@ function_t global_scope_t::look_for_precommand(scope_t& scope,
function_t global_scope_t::look_for_command(scope_t& scope, function_t global_scope_t::look_for_command(scope_t& scope,
const string& verb) const string& verb)
{ {
if (expr_t::ptr_op_t def = scope.lookup(string("cmd_") + verb)) if (expr_t::ptr_op_t def = scope.lookup(string(CMD_PREFIX) + verb))
return def->as_function(); return def->as_function();
else else
return function_t(); return function_t();
@ -361,7 +361,7 @@ void global_scope_t::normalize_report_options(const string& verb)
report_t& rep(report()); report_t& rep(report());
// jww (2009-02-09): These global are a hack, but hard to avoid // jww (2009-02-09): These global are a hack, but hard to avoid.
item_t::use_effective_date = rep.HANDLED(effective); item_t::use_effective_date = rep.HANDLED(effective);
if (rep.HANDLED(date_format_)) { if (rep.HANDLED(date_format_)) {
@ -369,14 +369,15 @@ void global_scope_t::normalize_report_options(const string& verb)
output_date_format = rep.HANDLER(date_format_).str(); output_date_format = rep.HANDLER(date_format_).str();
} }
// jww (2008-08-14): This code really needs to be rationalized away // jww (2008-08-14): This code really needs to be rationalized away for 3.0.
// for 3.0. // 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 == "entry" || verb == "dump") { if (verb == "print" || verb == "entry" || verb == "dump") {
rep.HANDLER(related).on_only(); rep.HANDLER(related).on_only();
rep.HANDLER(related_all).on_only(); rep.HANDLER(related_all).on_only();
} }
else if (verb == "equity") { else if (verb == "equity") {
rep.HANDLER(subtotal).on_only(); rep.HANDLER(equity).on_only();
} }
else if (rep.HANDLED(related)) { else if (rep.HANDLED(related)) {
if (verb[0] == 'r') { if (verb[0] == 'r') {

View file

@ -304,82 +304,4 @@ void format_accounts::operator()(account_t& account)
posted_accounts.push_back(&account); posted_accounts.push_back(&account);
} }
format_equity::format_equity(report_t& _report, const string& _format)
: format_accounts(_report)
{
const char * f = _format.c_str();
if (const char * p = std::strstr(f, "%/")) {
first_line_format.parse(string(f, 0, p - f));
next_lines_format.parse(string(p + 2));
} else {
first_line_format.parse(_format);
next_lines_format.parse(_format);
}
entry_t header_entry;
header_entry.payee = "Opening Balances";
header_entry._date = CURRENT_DATE();
bind_scope_t bound_scope(report, header_entry);
first_line_format.format(report.output_stream, bound_scope);
}
void format_equity::flush()
{
account_t summary(NULL, "Equity:Opening Balances");
account_t::xdata_t& xdata(summary.xdata());
std::ostream& out(report.output_stream);
xdata.value = total.negate();
if (total.type() >= value_t::BALANCE) {
const balance_t * bal;
if (total.is_type(value_t::BALANCE))
bal = &(total.as_balance());
else
assert(false);
foreach (balance_t::amounts_map::value_type pair, bal->amounts) {
xdata.value = pair.second;
xdata.value.negate();
bind_scope_t bound_scope(report, summary);
next_lines_format.format(out, bound_scope);
}
} else {
bind_scope_t bound_scope(report, summary);
next_lines_format.format(out, bound_scope);
}
out.flush();
}
void format_equity::post_account(account_t& account)
{
std::ostream& out(report.output_stream);
if (! account.has_flags(ACCOUNT_EXT_MATCHING))
return;
value_t val = account.xdata().value;
if (val.type() >= value_t::BALANCE) {
const balance_t * bal;
if (val.is_type(value_t::BALANCE))
bal = &(val.as_balance());
else
assert(false);
foreach (balance_t::amounts_map::value_type pair, bal->amounts) {
account.xdata().value = pair.second;
bind_scope_t bound_scope(report, account);
next_lines_format.format(out, bound_scope);
}
account.xdata().value = val;
} else {
bind_scope_t bound_scope(report, account);
next_lines_format.format(out, bound_scope);
}
total += val;
}
} // namespace ledger } // namespace ledger

View file

@ -192,29 +192,6 @@ public:
virtual void operator()(account_t& account); virtual void operator()(account_t& account);
}; };
/**
* @brief Brief
*
* Long.
*/
class format_equity : public format_accounts
{
format_t first_line_format;
format_t next_lines_format;
mutable value_t total;
public:
format_equity(report_t& _report,
const string& _format);
virtual ~format_equity() {
TRACE_DTOR(format_equity);
}
virtual void post_account(account_t& account);
virtual void flush();
};
} // namespace ledger } // namespace ledger
#endif // _OUTPUT_H #endif // _OUTPUT_H

View file

@ -356,7 +356,7 @@ option_t<report_t> * report_t::lookup_option(const char * p)
OPT(effective); OPT(effective);
else OPT(empty); else OPT(empty);
else OPT_(end_); else OPT_(end_);
else OPT(equity_format_); else OPT(equity);
break; break;
case 'f': case 'f':
OPT(flat); OPT(flat);
@ -475,10 +475,10 @@ expr_t::ptr_op_t report_t::lookup(const string& name)
break; break;
case 'c': case 'c':
if (WANT_CMD()) { p += CMD_PREFIX_LEN; if (WANT_CMD()) { const char * q = p + CMD_PREFIX_LEN;
switch (*p) { switch (*q) {
case 'b': case 'b':
if (*(p + 1) == '\0' || is_eq(p, "bal") || is_eq(p, "balance")) if (*(q + 1) == '\0' || is_eq(q, "bal") || is_eq(q, "balance"))
return expr_t::op_t::wrap_functor return expr_t::op_t::wrap_functor
(reporter<account_t, acct_handler_ptr, &report_t::accounts_report> (reporter<account_t, acct_handler_ptr, &report_t::accounts_report>
(new format_accounts(*this, report_format(HANDLER(balance_format_)), (new format_accounts(*this, report_format(HANDLER(balance_format_)),
@ -486,7 +486,7 @@ expr_t::ptr_op_t report_t::lookup(const string& name)
break; break;
case 'c': case 'c':
if (is_eq(p, "csv")) if (is_eq(q, "csv"))
return WRAP_FUNCTOR return WRAP_FUNCTOR
(reporter<> (reporter<>
(new format_xacts(*this, report_format(HANDLER(csv_format_))), (new format_xacts(*this, report_format(HANDLER(csv_format_))),
@ -494,30 +494,30 @@ expr_t::ptr_op_t report_t::lookup(const string& name)
break; break;
case 'e': case 'e':
if (is_eq(p, "equity")) if (is_eq(q, "equity"))
return expr_t::op_t::wrap_functor return WRAP_FUNCTOR
(reporter<account_t, acct_handler_ptr, &report_t::accounts_report> (reporter<>
(new format_equity(*this, report_format(HANDLER(print_format_))), (new format_xacts(*this, report_format(HANDLER(print_format_))),
*this)); *this));
else if (is_eq(p, "entry")) else if (is_eq(q, "entry"))
return WRAP_FUNCTOR(entry_command); return WRAP_FUNCTOR(entry_command);
else if (is_eq(p, "emacs")) else if (is_eq(q, "emacs"))
return WRAP_FUNCTOR return WRAP_FUNCTOR
(reporter<>(new format_emacs_xacts(output_stream), *this)); (reporter<>(new format_emacs_xacts(output_stream), *this));
break; break;
case 'p': case 'p':
if (*(p + 1) == '\0' || is_eq(p, "print")) if (*(q + 1) == '\0' || is_eq(q, "print"))
return WRAP_FUNCTOR return WRAP_FUNCTOR
(reporter<> (reporter<>
(new format_xacts(*this, report_format(HANDLER(print_format_))), (new format_xacts(*this, report_format(HANDLER(print_format_))),
*this)); *this));
else if (is_eq(p, "prices")) else if (is_eq(q, "prices"))
return expr_t::op_t::wrap_functor return expr_t::op_t::wrap_functor
(reporter<xact_t, xact_handler_ptr, &report_t::commodities_report> (reporter<xact_t, xact_handler_ptr, &report_t::commodities_report>
(new format_xacts(*this, report_format(HANDLER(prices_format_))), (new format_xacts(*this, report_format(HANDLER(prices_format_))),
*this)); *this));
else if (is_eq(p, "pricesdb")) else if (is_eq(q, "pricesdb"))
return expr_t::op_t::wrap_functor return expr_t::op_t::wrap_functor
(reporter<xact_t, xact_handler_ptr, &report_t::commodities_report> (reporter<xact_t, xact_handler_ptr, &report_t::commodities_report>
(new format_xacts(*this, report_format(HANDLER(pricesdb_format_))), (new format_xacts(*this, report_format(HANDLER(pricesdb_format_))),
@ -525,17 +525,17 @@ expr_t::ptr_op_t report_t::lookup(const string& name)
break; break;
case 'r': case 'r':
if (*(p + 1) == '\0' || is_eq(p, "reg") || is_eq(p, "register")) if (*(q + 1) == '\0' || is_eq(q, "reg") || is_eq(q, "register"))
return WRAP_FUNCTOR return WRAP_FUNCTOR
(reporter<> (reporter<>
(new format_xacts(*this, report_format(HANDLER(register_format_))), (new format_xacts(*this, report_format(HANDLER(register_format_))),
*this)); *this));
else if (is_eq(p, "reload")) else if (is_eq(q, "reload"))
return MAKE_FUNCTOR(report_t::reload_command); return MAKE_FUNCTOR(report_t::reload_command);
break; break;
case 's': case 's':
if (is_eq(p, "stats") || is_eq(p, "stat")) if (is_eq(q, "stats") || is_eq(q, "stat"))
return WRAP_FUNCTOR(reporter<>(new gather_statistics(*this), *this)); return WRAP_FUNCTOR(reporter<>(new gather_statistics(*this), *this));
break; break;
} }
@ -565,8 +565,8 @@ expr_t::ptr_op_t report_t::lookup(const string& name)
break; break;
case 'o': case 'o':
if (WANT_OPT()) { p += OPT_PREFIX_LEN; if (WANT_OPT()) { const char * q = p + OPT_PREFIX_LEN;
if (option_t<report_t> * handler = lookup_option(p)) if (option_t<report_t> * handler = lookup_option(q))
return MAKE_OPT_HANDLER(report_t, handler); return MAKE_OPT_HANDLER(report_t, handler);
} }
else if (is_eq(p, "options")) { else if (is_eq(p, "options")) {
@ -575,28 +575,28 @@ expr_t::ptr_op_t report_t::lookup(const string& name)
break; break;
case 'p': case 'p':
if (WANT_PRECMD()) { p += PRECMD_PREFIX_LEN; if (WANT_PRECMD()) { const char * q = p + PRECMD_PREFIX_LEN;
switch (*p) { switch (*q) {
case 'a': case 'a':
if (is_eq(p, "args")) if (is_eq(q, "args"))
return WRAP_FUNCTOR(args_command); return WRAP_FUNCTOR(args_command);
break; break;
case 'e': case 'e':
if (is_eq(p, "eval")) if (is_eq(q, "eval"))
return WRAP_FUNCTOR(eval_command); return WRAP_FUNCTOR(eval_command);
break; break;
case 'f': case 'f':
if (is_eq(p, "format")) if (is_eq(q, "format"))
return WRAP_FUNCTOR(format_command); return WRAP_FUNCTOR(format_command);
break; break;
case 'p': case 'p':
if (is_eq(p, "parse")) if (is_eq(q, "parse"))
return WRAP_FUNCTOR(parse_command); return WRAP_FUNCTOR(parse_command);
else if (is_eq(p, "period")) else if (is_eq(q, "period"))
return WRAP_FUNCTOR(period_command); return WRAP_FUNCTOR(period_command);
break; break;
case 't': case 't':
if (is_eq(p, "template")) if (is_eq(q, "template"))
return WRAP_FUNCTOR(template_command); return WRAP_FUNCTOR(template_command);
break; break;
} }

View file

@ -354,10 +354,7 @@ public:
#endif #endif
}); });
OPTION__(report_t, equity_format_, CTOR(report_t, equity_format_) { OPTION(report_t, equity);
on("\n%D %Y%C%P\n%/ %-34W %12t\n");
});
OPTION(report_t, flat); OPTION(report_t, flat);
OPTION(report_t, forecast_); OPTION(report_t, forecast_);
OPTION(report_t, format_); // -F OPTION(report_t, format_); // -F