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:
parent
f2f52066d2
commit
c1b25fcf86
8 changed files with 110 additions and 140 deletions
|
|
@ -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
|
||||
// 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));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -516,7 +516,7 @@ void interval_xacts::operator()(xact_t& xact)
|
|||
|
||||
xact_temps.push_back(xact_t(&empty_account));
|
||||
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_entry.add_xact(&null_xact);
|
||||
|
||||
|
|
@ -536,6 +536,49 @@ void interval_xacts::operator()(xact_t& 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()
|
||||
{
|
||||
TRACE_DTOR(by_payee_xacts);
|
||||
|
|
|
|||
|
|
@ -611,6 +611,34 @@ public:
|
|||
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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -339,7 +339,7 @@ void global_scope_t::normalize_session_options()
|
|||
function_t global_scope_t::look_for_precommand(scope_t& scope,
|
||||
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();
|
||||
else
|
||||
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,
|
||||
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();
|
||||
else
|
||||
return function_t();
|
||||
|
|
@ -361,7 +361,7 @@ void global_scope_t::normalize_report_options(const string& verb)
|
|||
|
||||
report_t& rep(report());
|
||||
|
||||
// jww (2009-02-09): These global are a hack, but hard to avoid
|
||||
// jww (2009-02-09): These global are a hack, but hard to avoid.
|
||||
item_t::use_effective_date = rep.HANDLED(effective);
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
// jww (2008-08-14): This code really needs to be rationalized away
|
||||
// for 3.0.
|
||||
// jww (2008-08-14): This code really needs to be rationalized away 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") {
|
||||
rep.HANDLER(related).on_only();
|
||||
rep.HANDLER(related_all).on_only();
|
||||
}
|
||||
else if (verb == "equity") {
|
||||
rep.HANDLER(subtotal).on_only();
|
||||
rep.HANDLER(equity).on_only();
|
||||
}
|
||||
else if (rep.HANDLED(related)) {
|
||||
if (verb[0] == 'r') {
|
||||
|
|
|
|||
|
|
@ -304,82 +304,4 @@ void format_accounts::operator()(account_t& 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
|
||||
|
|
|
|||
23
src/output.h
23
src/output.h
|
|
@ -192,29 +192,6 @@ public:
|
|||
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
|
||||
|
||||
#endif // _OUTPUT_H
|
||||
|
|
|
|||
|
|
@ -356,7 +356,7 @@ option_t<report_t> * report_t::lookup_option(const char * p)
|
|||
OPT(effective);
|
||||
else OPT(empty);
|
||||
else OPT_(end_);
|
||||
else OPT(equity_format_);
|
||||
else OPT(equity);
|
||||
break;
|
||||
case 'f':
|
||||
OPT(flat);
|
||||
|
|
@ -475,10 +475,10 @@ expr_t::ptr_op_t report_t::lookup(const string& name)
|
|||
break;
|
||||
|
||||
case 'c':
|
||||
if (WANT_CMD()) { p += CMD_PREFIX_LEN;
|
||||
switch (*p) {
|
||||
if (WANT_CMD()) { const char * q = p + CMD_PREFIX_LEN;
|
||||
switch (*q) {
|
||||
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
|
||||
(reporter<account_t, acct_handler_ptr, &report_t::accounts_report>
|
||||
(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;
|
||||
|
||||
case 'c':
|
||||
if (is_eq(p, "csv"))
|
||||
if (is_eq(q, "csv"))
|
||||
return WRAP_FUNCTOR
|
||||
(reporter<>
|
||||
(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;
|
||||
|
||||
case 'e':
|
||||
if (is_eq(p, "equity"))
|
||||
return expr_t::op_t::wrap_functor
|
||||
(reporter<account_t, acct_handler_ptr, &report_t::accounts_report>
|
||||
(new format_equity(*this, report_format(HANDLER(print_format_))),
|
||||
if (is_eq(q, "equity"))
|
||||
return WRAP_FUNCTOR
|
||||
(reporter<>
|
||||
(new format_xacts(*this, report_format(HANDLER(print_format_))),
|
||||
*this));
|
||||
else if (is_eq(p, "entry"))
|
||||
else if (is_eq(q, "entry"))
|
||||
return WRAP_FUNCTOR(entry_command);
|
||||
else if (is_eq(p, "emacs"))
|
||||
else if (is_eq(q, "emacs"))
|
||||
return WRAP_FUNCTOR
|
||||
(reporter<>(new format_emacs_xacts(output_stream), *this));
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
if (*(p + 1) == '\0' || is_eq(p, "print"))
|
||||
if (*(q + 1) == '\0' || is_eq(q, "print"))
|
||||
return WRAP_FUNCTOR
|
||||
(reporter<>
|
||||
(new format_xacts(*this, report_format(HANDLER(print_format_))),
|
||||
*this));
|
||||
else if (is_eq(p, "prices"))
|
||||
else if (is_eq(q, "prices"))
|
||||
return expr_t::op_t::wrap_functor
|
||||
(reporter<xact_t, xact_handler_ptr, &report_t::commodities_report>
|
||||
(new format_xacts(*this, report_format(HANDLER(prices_format_))),
|
||||
*this));
|
||||
else if (is_eq(p, "pricesdb"))
|
||||
else if (is_eq(q, "pricesdb"))
|
||||
return expr_t::op_t::wrap_functor
|
||||
(reporter<xact_t, xact_handler_ptr, &report_t::commodities_report>
|
||||
(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;
|
||||
|
||||
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
|
||||
(reporter<>
|
||||
(new format_xacts(*this, report_format(HANDLER(register_format_))),
|
||||
*this));
|
||||
else if (is_eq(p, "reload"))
|
||||
else if (is_eq(q, "reload"))
|
||||
return MAKE_FUNCTOR(report_t::reload_command);
|
||||
break;
|
||||
|
||||
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));
|
||||
break;
|
||||
}
|
||||
|
|
@ -565,8 +565,8 @@ expr_t::ptr_op_t report_t::lookup(const string& name)
|
|||
break;
|
||||
|
||||
case 'o':
|
||||
if (WANT_OPT()) { p += OPT_PREFIX_LEN;
|
||||
if (option_t<report_t> * handler = lookup_option(p))
|
||||
if (WANT_OPT()) { const char * q = p + OPT_PREFIX_LEN;
|
||||
if (option_t<report_t> * handler = lookup_option(q))
|
||||
return MAKE_OPT_HANDLER(report_t, handler);
|
||||
}
|
||||
else if (is_eq(p, "options")) {
|
||||
|
|
@ -575,28 +575,28 @@ expr_t::ptr_op_t report_t::lookup(const string& name)
|
|||
break;
|
||||
|
||||
case 'p':
|
||||
if (WANT_PRECMD()) { p += PRECMD_PREFIX_LEN;
|
||||
switch (*p) {
|
||||
if (WANT_PRECMD()) { const char * q = p + PRECMD_PREFIX_LEN;
|
||||
switch (*q) {
|
||||
case 'a':
|
||||
if (is_eq(p, "args"))
|
||||
if (is_eq(q, "args"))
|
||||
return WRAP_FUNCTOR(args_command);
|
||||
break;
|
||||
case 'e':
|
||||
if (is_eq(p, "eval"))
|
||||
if (is_eq(q, "eval"))
|
||||
return WRAP_FUNCTOR(eval_command);
|
||||
break;
|
||||
case 'f':
|
||||
if (is_eq(p, "format"))
|
||||
if (is_eq(q, "format"))
|
||||
return WRAP_FUNCTOR(format_command);
|
||||
break;
|
||||
case 'p':
|
||||
if (is_eq(p, "parse"))
|
||||
if (is_eq(q, "parse"))
|
||||
return WRAP_FUNCTOR(parse_command);
|
||||
else if (is_eq(p, "period"))
|
||||
else if (is_eq(q, "period"))
|
||||
return WRAP_FUNCTOR(period_command);
|
||||
break;
|
||||
case 't':
|
||||
if (is_eq(p, "template"))
|
||||
if (is_eq(q, "template"))
|
||||
return WRAP_FUNCTOR(template_command);
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -354,10 +354,7 @@ public:
|
|||
#endif
|
||||
});
|
||||
|
||||
OPTION__(report_t, equity_format_, CTOR(report_t, equity_format_) {
|
||||
on("\n%D %Y%C%P\n%/ %-34W %12t\n");
|
||||
});
|
||||
|
||||
OPTION(report_t, equity);
|
||||
OPTION(report_t, flat);
|
||||
OPTION(report_t, forecast_);
|
||||
OPTION(report_t, format_); // -F
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue