New: --group-by=EXPR and --group-title-format=FMT
The --group-by option allows for most reports to be split up into sections based on the varying value of EXPR. For example, to see register subtotals by payee, use: ledger reg --group-by=payee -s This works for separated balances too: ledger bal --group-by=payee Another interesting possibility is seeing a register of all the accounts affected by a related account: ledger reg -r --group-by=payee The option --group-title-format can be used to add a separator bar to the group titles. The option --no-titles can be used to drop titles altogether.
This commit is contained in:
parent
a41d33fba3
commit
647d4aac2f
8 changed files with 299 additions and 104 deletions
126
src/chain.cc
126
src/chain.cc
|
|
@ -39,6 +39,73 @@
|
||||||
|
|
||||||
namespace ledger {
|
namespace ledger {
|
||||||
|
|
||||||
|
post_handler_ptr chain_pre_post_handlers(report_t& report,
|
||||||
|
post_handler_ptr base_handler)
|
||||||
|
{
|
||||||
|
post_handler_ptr handler(base_handler);
|
||||||
|
|
||||||
|
// anonymize_posts removes all meaningful information from xact payee's and
|
||||||
|
// account names, for the sake of creating useful bug reports.
|
||||||
|
if (report.HANDLED(anon))
|
||||||
|
handler.reset(new anonymize_posts(handler));
|
||||||
|
|
||||||
|
// This filter_posts will only pass through posts matching the `predicate'.
|
||||||
|
if (report.HANDLED(limit_)) {
|
||||||
|
DEBUG("report.predicate",
|
||||||
|
"Report predicate expression = " << report.HANDLER(limit_).str());
|
||||||
|
handler.reset(new filter_posts
|
||||||
|
(handler, predicate_t(report.HANDLER(limit_).str(),
|
||||||
|
report.what_to_keep()),
|
||||||
|
report));
|
||||||
|
}
|
||||||
|
|
||||||
|
// budget_posts takes a set of posts from a data file and uses them to
|
||||||
|
// generate "budget posts" which balance against the reported posts.
|
||||||
|
//
|
||||||
|
// forecast_posts is a lot like budget_posts, except that it adds xacts
|
||||||
|
// only for the future, and does not balance them against anything but the
|
||||||
|
// future balance.
|
||||||
|
|
||||||
|
if (report.budget_flags != BUDGET_NO_BUDGET) {
|
||||||
|
budget_posts * budget_handler = new budget_posts(handler,
|
||||||
|
report.budget_flags);
|
||||||
|
budget_handler->add_period_xacts(report.session.journal->period_xacts);
|
||||||
|
handler.reset(budget_handler);
|
||||||
|
|
||||||
|
// Apply this before the budget handler, so that only matching posts are
|
||||||
|
// calculated toward the budget. The use of filter_posts above will
|
||||||
|
// further clean the results so that no automated posts that don't match
|
||||||
|
// the filter get reported.
|
||||||
|
if (report.HANDLED(limit_))
|
||||||
|
handler.reset(new filter_posts
|
||||||
|
(handler, predicate_t(report.HANDLER(limit_).str(),
|
||||||
|
report.what_to_keep()),
|
||||||
|
report));
|
||||||
|
}
|
||||||
|
else if (report.HANDLED(forecast_while_)) {
|
||||||
|
forecast_posts * forecast_handler
|
||||||
|
= new forecast_posts(handler,
|
||||||
|
predicate_t(report.HANDLER(forecast_while_).str(),
|
||||||
|
report.what_to_keep()),
|
||||||
|
report,
|
||||||
|
report.HANDLED(forecast_years_) ?
|
||||||
|
static_cast<std::size_t>
|
||||||
|
(report.HANDLER(forecast_years_).value.to_long()) :
|
||||||
|
5UL);
|
||||||
|
forecast_handler->add_period_xacts(report.session.journal->period_xacts);
|
||||||
|
handler.reset(forecast_handler);
|
||||||
|
|
||||||
|
// See above, under budget_posts.
|
||||||
|
if (report.HANDLED(limit_))
|
||||||
|
handler.reset(new filter_posts
|
||||||
|
(handler, predicate_t(report.HANDLER(limit_).str(),
|
||||||
|
report.what_to_keep()),
|
||||||
|
report));
|
||||||
|
}
|
||||||
|
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
|
||||||
post_handler_ptr chain_post_handlers(report_t& report,
|
post_handler_ptr chain_post_handlers(report_t& report,
|
||||||
post_handler_ptr base_handler,
|
post_handler_ptr base_handler,
|
||||||
bool for_accounts_report)
|
bool for_accounts_report)
|
||||||
|
|
@ -189,65 +256,6 @@ post_handler_ptr chain_post_handlers(report_t& report,
|
||||||
if (report.HANDLED(related))
|
if (report.HANDLED(related))
|
||||||
handler.reset(new related_posts(handler, report.HANDLED(related_all)));
|
handler.reset(new related_posts(handler, report.HANDLED(related_all)));
|
||||||
|
|
||||||
// anonymize_posts removes all meaningful information from xact payee's and
|
|
||||||
// account names, for the sake of creating useful bug reports.
|
|
||||||
if (report.HANDLED(anon))
|
|
||||||
handler.reset(new anonymize_posts(handler));
|
|
||||||
|
|
||||||
// This filter_posts will only pass through posts matching the `predicate'.
|
|
||||||
if (report.HANDLED(limit_)) {
|
|
||||||
DEBUG("report.predicate",
|
|
||||||
"Report predicate expression = " << report.HANDLER(limit_).str());
|
|
||||||
handler.reset(new filter_posts
|
|
||||||
(handler, predicate_t(report.HANDLER(limit_).str(),
|
|
||||||
report.what_to_keep()),
|
|
||||||
report));
|
|
||||||
}
|
|
||||||
|
|
||||||
// budget_posts takes a set of posts from a data file and uses them to
|
|
||||||
// generate "budget posts" which balance against the reported posts.
|
|
||||||
//
|
|
||||||
// forecast_posts is a lot like budget_posts, except that it adds xacts
|
|
||||||
// only for the future, and does not balance them against anything but the
|
|
||||||
// future balance.
|
|
||||||
|
|
||||||
if (report.budget_flags != BUDGET_NO_BUDGET) {
|
|
||||||
budget_posts * budget_handler = new budget_posts(handler,
|
|
||||||
report.budget_flags);
|
|
||||||
budget_handler->add_period_xacts(report.session.journal->period_xacts);
|
|
||||||
handler.reset(budget_handler);
|
|
||||||
|
|
||||||
// Apply this before the budget handler, so that only matching posts are
|
|
||||||
// calculated toward the budget. The use of filter_posts above will
|
|
||||||
// further clean the results so that no automated posts that don't match
|
|
||||||
// the filter get reported.
|
|
||||||
if (report.HANDLED(limit_))
|
|
||||||
handler.reset(new filter_posts
|
|
||||||
(handler, predicate_t(report.HANDLER(limit_).str(),
|
|
||||||
report.what_to_keep()),
|
|
||||||
report));
|
|
||||||
}
|
|
||||||
else if (report.HANDLED(forecast_while_)) {
|
|
||||||
forecast_posts * forecast_handler
|
|
||||||
= new forecast_posts(handler,
|
|
||||||
predicate_t(report.HANDLER(forecast_while_).str(),
|
|
||||||
report.what_to_keep()),
|
|
||||||
report,
|
|
||||||
report.HANDLED(forecast_years_) ?
|
|
||||||
static_cast<std::size_t>
|
|
||||||
(report.HANDLER(forecast_years_).value.to_long()) :
|
|
||||||
5UL);
|
|
||||||
forecast_handler->add_period_xacts(report.session.journal->period_xacts);
|
|
||||||
handler.reset(forecast_handler);
|
|
||||||
|
|
||||||
// See above, under budget_posts.
|
|
||||||
if (report.HANDLED(limit_))
|
|
||||||
handler.reset(new filter_posts
|
|
||||||
(handler, predicate_t(report.HANDLER(limit_).str(),
|
|
||||||
report.what_to_keep()),
|
|
||||||
report));
|
|
||||||
}
|
|
||||||
|
|
||||||
return handler;
|
return handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
14
src/chain.h
14
src/chain.h
|
|
@ -91,11 +91,25 @@ typedef shared_ptr<item_handler<post_t> > post_handler_ptr;
|
||||||
typedef shared_ptr<item_handler<account_t> > acct_handler_ptr;
|
typedef shared_ptr<item_handler<account_t> > acct_handler_ptr;
|
||||||
|
|
||||||
class report_t;
|
class report_t;
|
||||||
|
|
||||||
|
post_handler_ptr
|
||||||
|
chain_pre_post_handlers(report_t& report,
|
||||||
|
post_handler_ptr base_handler);
|
||||||
|
|
||||||
post_handler_ptr
|
post_handler_ptr
|
||||||
chain_post_handlers(report_t& report,
|
chain_post_handlers(report_t& report,
|
||||||
post_handler_ptr base_handler,
|
post_handler_ptr base_handler,
|
||||||
bool for_accounts_report = false);
|
bool for_accounts_report = false);
|
||||||
|
|
||||||
|
inline post_handler_ptr
|
||||||
|
chain_handlers(report_t& report,
|
||||||
|
post_handler_ptr handler,
|
||||||
|
bool for_accounts_report = false) {
|
||||||
|
handler = chain_post_handlers(report, handler, for_accounts_report);
|
||||||
|
handler = chain_pre_post_handlers(report, handler);
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ledger
|
} // namespace ledger
|
||||||
|
|
||||||
#endif // _CHAIN_H
|
#endif // _CHAIN_H
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ format_posts::format_posts(report_t& _report,
|
||||||
const optional<string>& _prepend_format,
|
const optional<string>& _prepend_format,
|
||||||
std::size_t _prepend_width)
|
std::size_t _prepend_width)
|
||||||
: report(_report), prepend_width(_prepend_width),
|
: report(_report), prepend_width(_prepend_width),
|
||||||
last_xact(NULL), last_post(NULL)
|
last_xact(NULL), last_post(NULL), first_report_title(true)
|
||||||
{
|
{
|
||||||
TRACE_CTOR(format_posts, "report&, const string&, bool");
|
TRACE_CTOR(format_posts, "report&, const string&, bool");
|
||||||
|
|
||||||
|
|
@ -78,12 +78,30 @@ void format_posts::flush()
|
||||||
|
|
||||||
void format_posts::operator()(post_t& post)
|
void format_posts::operator()(post_t& post)
|
||||||
{
|
{
|
||||||
std::ostream& out(report.output_stream);
|
|
||||||
|
|
||||||
if (! post.has_xdata() ||
|
if (! post.has_xdata() ||
|
||||||
! post.xdata().has_flags(POST_EXT_DISPLAYED)) {
|
! post.xdata().has_flags(POST_EXT_DISPLAYED)) {
|
||||||
|
std::ostream& out(report.output_stream);
|
||||||
|
|
||||||
bind_scope_t bound_scope(report, post);
|
bind_scope_t bound_scope(report, post);
|
||||||
|
|
||||||
|
if (! report_title.empty()) {
|
||||||
|
if (first_report_title)
|
||||||
|
first_report_title = false;
|
||||||
|
else
|
||||||
|
out << '\n';
|
||||||
|
|
||||||
|
value_scope_t val_scope(string_value(report_title));
|
||||||
|
bind_scope_t inner_scope(bound_scope, val_scope);
|
||||||
|
|
||||||
|
format_t group_title_format;
|
||||||
|
group_title_format
|
||||||
|
.parse_format(report.HANDLER(group_title_format_).str());
|
||||||
|
|
||||||
|
out << group_title_format(inner_scope);
|
||||||
|
|
||||||
|
report_title = "";
|
||||||
|
}
|
||||||
|
|
||||||
if (prepend_format) {
|
if (prepend_format) {
|
||||||
out.width(prepend_width);
|
out.width(prepend_width);
|
||||||
out << prepend_format(bound_scope);
|
out << prepend_format(bound_scope);
|
||||||
|
|
@ -113,7 +131,8 @@ format_accounts::format_accounts(report_t& _report,
|
||||||
const string& format,
|
const string& format,
|
||||||
const optional<string>& _prepend_format,
|
const optional<string>& _prepend_format,
|
||||||
std::size_t _prepend_width)
|
std::size_t _prepend_width)
|
||||||
: report(_report), prepend_width(_prepend_width), disp_pred()
|
: report(_report), prepend_width(_prepend_width), disp_pred(),
|
||||||
|
first_report_title(true)
|
||||||
{
|
{
|
||||||
TRACE_CTOR(format_accounts, "report&, const string&");
|
TRACE_CTOR(format_accounts, "report&, const string&");
|
||||||
|
|
||||||
|
|
@ -144,19 +163,37 @@ std::size_t format_accounts::post_account(account_t& account, const bool flat)
|
||||||
|
|
||||||
if (account.xdata().has_flags(ACCOUNT_EXT_TO_DISPLAY) &&
|
if (account.xdata().has_flags(ACCOUNT_EXT_TO_DISPLAY) &&
|
||||||
! account.xdata().has_flags(ACCOUNT_EXT_DISPLAYED)) {
|
! account.xdata().has_flags(ACCOUNT_EXT_DISPLAYED)) {
|
||||||
|
std::ostream& out(report.output_stream);
|
||||||
|
|
||||||
DEBUG("account.display", "Displaying account: " << account.fullname());
|
DEBUG("account.display", "Displaying account: " << account.fullname());
|
||||||
account.xdata().add_flags(ACCOUNT_EXT_DISPLAYED);
|
account.xdata().add_flags(ACCOUNT_EXT_DISPLAYED);
|
||||||
|
|
||||||
bind_scope_t bound_scope(report, account);
|
bind_scope_t bound_scope(report, account);
|
||||||
|
|
||||||
if (prepend_format) {
|
if (! report_title.empty()) {
|
||||||
static_cast<std::ostream&>(report.output_stream).width(prepend_width);
|
if (first_report_title)
|
||||||
static_cast<std::ostream&>(report.output_stream)
|
first_report_title = false;
|
||||||
<< prepend_format(bound_scope);
|
else
|
||||||
|
out << '\n';
|
||||||
|
|
||||||
|
value_scope_t val_scope(string_value(report_title));
|
||||||
|
bind_scope_t inner_scope(bound_scope, val_scope);
|
||||||
|
|
||||||
|
format_t group_title_format;
|
||||||
|
group_title_format
|
||||||
|
.parse_format(report.HANDLER(group_title_format_).str());
|
||||||
|
|
||||||
|
out << group_title_format(inner_scope);
|
||||||
|
|
||||||
|
report_title = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
static_cast<std::ostream&>(report.output_stream)
|
if (prepend_format) {
|
||||||
<< account_line_format(bound_scope);
|
out.width(prepend_width);
|
||||||
|
out << prepend_format(bound_scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
out << account_line_format(bound_scope);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
16
src/output.h
16
src/output.h
|
|
@ -64,6 +64,8 @@ protected:
|
||||||
std::size_t prepend_width;
|
std::size_t prepend_width;
|
||||||
xact_t * last_xact;
|
xact_t * last_xact;
|
||||||
post_t * last_post;
|
post_t * last_post;
|
||||||
|
bool first_report_title;
|
||||||
|
string report_title;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
format_posts(report_t& _report, const string& format,
|
format_posts(report_t& _report, const string& format,
|
||||||
|
|
@ -73,6 +75,10 @@ public:
|
||||||
TRACE_DTOR(format_posts);
|
TRACE_DTOR(format_posts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual void title(const string& str) {
|
||||||
|
report_title = str;
|
||||||
|
}
|
||||||
|
|
||||||
virtual void flush();
|
virtual void flush();
|
||||||
virtual void operator()(post_t& post);
|
virtual void operator()(post_t& post);
|
||||||
|
|
||||||
|
|
@ -80,6 +86,8 @@ public:
|
||||||
last_xact = NULL;
|
last_xact = NULL;
|
||||||
last_post = NULL;
|
last_post = NULL;
|
||||||
|
|
||||||
|
report_title = "";
|
||||||
|
|
||||||
item_handler<post_t>::clear();
|
item_handler<post_t>::clear();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -94,6 +102,8 @@ protected:
|
||||||
format_t prepend_format;
|
format_t prepend_format;
|
||||||
std::size_t prepend_width;
|
std::size_t prepend_width;
|
||||||
predicate_t disp_pred;
|
predicate_t disp_pred;
|
||||||
|
bool first_report_title;
|
||||||
|
string report_title;
|
||||||
|
|
||||||
std::list<account_t *> posted_accounts;
|
std::list<account_t *> posted_accounts;
|
||||||
|
|
||||||
|
|
@ -108,6 +118,10 @@ public:
|
||||||
std::pair<std::size_t, std::size_t>
|
std::pair<std::size_t, std::size_t>
|
||||||
mark_accounts(account_t& account, const bool flat);
|
mark_accounts(account_t& account, const bool flat);
|
||||||
|
|
||||||
|
virtual void title(const string& str) {
|
||||||
|
report_title = str;
|
||||||
|
}
|
||||||
|
|
||||||
virtual std::size_t post_account(account_t& account, const bool flat);
|
virtual std::size_t post_account(account_t& account, const bool flat);
|
||||||
virtual void flush();
|
virtual void flush();
|
||||||
|
|
||||||
|
|
@ -117,6 +131,8 @@ public:
|
||||||
disp_pred.mark_uncompiled();
|
disp_pred.mark_uncompiled();
|
||||||
posted_accounts.clear();
|
posted_accounts.clear();
|
||||||
|
|
||||||
|
report_title = "";
|
||||||
|
|
||||||
item_handler<account_t>::clear();
|
item_handler<account_t>::clear();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
12
src/print.cc
12
src/print.cc
|
|
@ -42,7 +42,7 @@ namespace ledger {
|
||||||
|
|
||||||
print_xacts::print_xacts(report_t& _report,
|
print_xacts::print_xacts(report_t& _report,
|
||||||
bool _print_raw)
|
bool _print_raw)
|
||||||
: report(_report), print_raw(_print_raw)
|
: report(_report), print_raw(_print_raw), first_title(true)
|
||||||
{
|
{
|
||||||
TRACE_CTOR(print_xacts, "report&, bool");
|
TRACE_CTOR(print_xacts, "report&, bool");
|
||||||
}
|
}
|
||||||
|
|
@ -221,6 +221,16 @@ namespace {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void print_xacts::title(const string&)
|
||||||
|
{
|
||||||
|
if (first_title) {
|
||||||
|
first_title = false;
|
||||||
|
} else {
|
||||||
|
std::ostream& out(report.output_stream);
|
||||||
|
out << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void print_xacts::flush()
|
void print_xacts::flush()
|
||||||
{
|
{
|
||||||
std::ostream& out(report.output_stream);
|
std::ostream& out(report.output_stream);
|
||||||
|
|
|
||||||
10
src/print.h
10
src/print.h
|
|
@ -62,6 +62,7 @@ protected:
|
||||||
xacts_present_map xacts_present;
|
xacts_present_map xacts_present;
|
||||||
xacts_list xacts;
|
xacts_list xacts;
|
||||||
bool print_raw;
|
bool print_raw;
|
||||||
|
bool first_title;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
print_xacts(report_t& _report, bool _print_raw = false);
|
print_xacts(report_t& _report, bool _print_raw = false);
|
||||||
|
|
@ -69,8 +70,17 @@ public:
|
||||||
TRACE_DTOR(print_xacts);
|
TRACE_DTOR(print_xacts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual void title(const string&);
|
||||||
|
|
||||||
virtual void flush();
|
virtual void flush();
|
||||||
virtual void operator()(post_t& post);
|
virtual void operator()(post_t& post);
|
||||||
|
|
||||||
|
virtual void clear() {
|
||||||
|
xacts_present.clear();
|
||||||
|
xacts.clear();
|
||||||
|
|
||||||
|
item_handler<post_t>::clear();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
150
src/report.cc
150
src/report.cc
|
|
@ -274,10 +274,35 @@ void report_t::parse_query_args(const value_t& args, const string& whence)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
struct posts_flusher
|
||||||
|
{
|
||||||
|
report_t& report;
|
||||||
|
post_handler_ptr handler;
|
||||||
|
|
||||||
|
posts_flusher(report_t& _report, post_handler_ptr _handler)
|
||||||
|
: report(_report), handler(_handler) {}
|
||||||
|
|
||||||
|
void operator()(const value_t&) {
|
||||||
|
report.session.journal->clear_xdata();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
void report_t::posts_report(post_handler_ptr handler)
|
void report_t::posts_report(post_handler_ptr handler)
|
||||||
{
|
{
|
||||||
|
handler = chain_post_handlers(*this, handler);
|
||||||
|
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));
|
||||||
|
handler = post_handler_ptr(splitter.release());
|
||||||
|
}
|
||||||
|
handler = chain_pre_post_handlers(*this, handler);
|
||||||
|
|
||||||
journal_posts_iterator walker(*session.journal.get());
|
journal_posts_iterator walker(*session.journal.get());
|
||||||
pass_down_posts(chain_post_handlers(*this, handler), walker);
|
pass_down_posts(handler, walker);
|
||||||
|
|
||||||
session.journal->clear_xdata();
|
session.journal->clear_xdata();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -285,66 +310,121 @@ void report_t::generate_report(post_handler_ptr handler)
|
||||||
{
|
{
|
||||||
HANDLER(limit_).on(string("#generate"), "actual");
|
HANDLER(limit_).on(string("#generate"), "actual");
|
||||||
|
|
||||||
|
handler = chain_handlers(*this, handler);
|
||||||
|
|
||||||
generate_posts_iterator walker
|
generate_posts_iterator walker
|
||||||
(session, HANDLED(seed_) ?
|
(session, HANDLED(seed_) ?
|
||||||
static_cast<unsigned int>(HANDLER(seed_).value.to_long()) : 0,
|
static_cast<unsigned int>(HANDLER(seed_).value.to_long()) : 0,
|
||||||
HANDLED(head_) ?
|
HANDLED(head_) ?
|
||||||
static_cast<unsigned int>(HANDLER(head_).value.to_long()) : 50);
|
static_cast<unsigned int>(HANDLER(head_).value.to_long()) : 50);
|
||||||
|
|
||||||
pass_down_posts(chain_post_handlers(*this, handler), walker);
|
pass_down_posts(handler, walker);
|
||||||
}
|
}
|
||||||
|
|
||||||
void report_t::xact_report(post_handler_ptr handler, xact_t& xact)
|
void report_t::xact_report(post_handler_ptr handler, xact_t& xact)
|
||||||
{
|
{
|
||||||
|
handler = chain_handlers(*this, handler);
|
||||||
|
|
||||||
xact_posts_iterator walker(xact);
|
xact_posts_iterator walker(xact);
|
||||||
pass_down_posts(chain_post_handlers(*this, handler), walker);
|
pass_down_posts(handler, walker);
|
||||||
|
|
||||||
xact.clear_xdata();
|
xact.clear_xdata();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
struct accounts_title_printer
|
||||||
|
{
|
||||||
|
report_t& report;
|
||||||
|
acct_handler_ptr handler;
|
||||||
|
|
||||||
|
accounts_title_printer(report_t& _report, acct_handler_ptr _handler)
|
||||||
|
: report(_report), handler(_handler) {}
|
||||||
|
|
||||||
|
void operator()(const value_t& val)
|
||||||
|
{
|
||||||
|
if (! report.HANDLED(no_titles)) {
|
||||||
|
std::ostringstream buf;
|
||||||
|
val.print(buf);
|
||||||
|
handler->title(buf.str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct accounts_flusher
|
||||||
|
{
|
||||||
|
report_t& report;
|
||||||
|
acct_handler_ptr handler;
|
||||||
|
|
||||||
|
accounts_flusher(report_t& _report, acct_handler_ptr _handler)
|
||||||
|
: report(_report), handler(_handler) {}
|
||||||
|
|
||||||
|
void operator()(const value_t&)
|
||||||
|
{
|
||||||
|
report.HANDLER(amount_).expr.mark_uncompiled();
|
||||||
|
report.HANDLER(total_).expr.mark_uncompiled();
|
||||||
|
report.HANDLER(display_amount_).expr.mark_uncompiled();
|
||||||
|
report.HANDLER(display_total_).expr.mark_uncompiled();
|
||||||
|
report.HANDLER(revalued_total_).expr.mark_uncompiled();
|
||||||
|
|
||||||
|
scoped_ptr<accounts_iterator> iter;
|
||||||
|
if (! report.HANDLED(sort_)) {
|
||||||
|
iter.reset(new basic_accounts_iterator(*report.session.journal->master));
|
||||||
|
} else {
|
||||||
|
expr_t sort_expr(report.HANDLER(sort_).str());
|
||||||
|
sort_expr.set_context(&report);
|
||||||
|
iter.reset(new sorted_accounts_iterator(*report.session.journal->master,
|
||||||
|
sort_expr, report.HANDLED(flat)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (report.HANDLED(display_)) {
|
||||||
|
DEBUG("report.predicate",
|
||||||
|
"Display predicate = " << report.HANDLER(display_).str());
|
||||||
|
pass_down_accounts(handler, *iter.get(),
|
||||||
|
predicate_t(report.HANDLER(display_).str(),
|
||||||
|
report.what_to_keep()),
|
||||||
|
report);
|
||||||
|
} else {
|
||||||
|
pass_down_accounts(handler, *iter.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
report.session.journal->clear_xdata();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
void report_t::accounts_report(acct_handler_ptr handler)
|
void report_t::accounts_report(acct_handler_ptr handler)
|
||||||
{
|
{
|
||||||
journal_posts_iterator walker(*session.journal.get());
|
post_handler_ptr chain =
|
||||||
|
chain_post_handlers(*this, post_handler_ptr(new ignore_posts),
|
||||||
|
/* for_accounts_report= */ true);
|
||||||
|
if (HANDLED(group_by_)) {
|
||||||
|
std::auto_ptr<post_splitter>
|
||||||
|
splitter(new post_splitter(*this, chain, HANDLER(group_by_).expr));
|
||||||
|
|
||||||
|
splitter->set_preflush_func(accounts_title_printer(*this, handler));
|
||||||
|
splitter->set_postflush_func(accounts_flusher(*this, handler));
|
||||||
|
|
||||||
|
chain = post_handler_ptr(splitter.release());
|
||||||
|
}
|
||||||
|
chain = chain_pre_post_handlers(*this, chain);
|
||||||
|
|
||||||
// The lifetime of the chain object controls the lifetime of all temporary
|
// 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
|
// objects created within it during the call to pass_down_posts, which will
|
||||||
// be needed later by the pass_down_accounts.
|
// be needed later by the pass_down_accounts.
|
||||||
post_handler_ptr chain =
|
journal_posts_iterator walker(*session.journal.get());
|
||||||
chain_post_handlers(*this, post_handler_ptr(new ignore_posts), true);
|
|
||||||
pass_down_posts(chain, walker);
|
pass_down_posts(chain, walker);
|
||||||
|
|
||||||
HANDLER(amount_).expr.mark_uncompiled();
|
if (! HANDLED(group_by_))
|
||||||
HANDLER(total_).expr.mark_uncompiled();
|
accounts_flusher(*this, handler)(value_t());
|
||||||
HANDLER(display_amount_).expr.mark_uncompiled();
|
|
||||||
HANDLER(display_total_).expr.mark_uncompiled();
|
|
||||||
HANDLER(revalued_total_).expr.mark_uncompiled();
|
|
||||||
|
|
||||||
scoped_ptr<accounts_iterator> iter;
|
|
||||||
if (! HANDLED(sort_)) {
|
|
||||||
iter.reset(new basic_accounts_iterator(*session.journal->master));
|
|
||||||
} else {
|
|
||||||
expr_t sort_expr(HANDLER(sort_).str());
|
|
||||||
sort_expr.set_context(this);
|
|
||||||
iter.reset(new sorted_accounts_iterator(*session.journal->master,
|
|
||||||
sort_expr, HANDLED(flat)));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (HANDLED(display_)) {
|
|
||||||
DEBUG("report.predicate",
|
|
||||||
"Display predicate = " << HANDLER(display_).str());
|
|
||||||
pass_down_accounts(handler, *iter.get(),
|
|
||||||
predicate_t(HANDLER(display_).str(), what_to_keep()),
|
|
||||||
*this);
|
|
||||||
} else {
|
|
||||||
pass_down_accounts(handler, *iter.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
session.journal->clear_xdata();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void report_t::commodities_report(post_handler_ptr handler)
|
void report_t::commodities_report(post_handler_ptr handler)
|
||||||
{
|
{
|
||||||
|
handler = chain_handlers(*this, handler);
|
||||||
|
|
||||||
posts_commodities_iterator walker(*session.journal.get());
|
posts_commodities_iterator walker(*session.journal.get());
|
||||||
pass_down_posts(chain_post_handlers(*this, handler), walker);
|
pass_down_posts(handler, walker);
|
||||||
|
|
||||||
session.journal->clear_xdata();
|
session.journal->clear_xdata();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -888,6 +968,8 @@ option_t<report_t> * report_t::lookup_option(const char * p)
|
||||||
break;
|
break;
|
||||||
case 'g':
|
case 'g':
|
||||||
OPT(gain);
|
OPT(gain);
|
||||||
|
else OPT(group_by_);
|
||||||
|
else OPT(group_title_format_);
|
||||||
break;
|
break;
|
||||||
case 'h':
|
case 'h':
|
||||||
OPT(head_);
|
OPT(head_);
|
||||||
|
|
|
||||||
18
src/report.h
18
src/report.h
|
|
@ -256,6 +256,8 @@ public:
|
||||||
HANDLER(forecast_years_).report(out);
|
HANDLER(forecast_years_).report(out);
|
||||||
HANDLER(format_).report(out);
|
HANDLER(format_).report(out);
|
||||||
HANDLER(gain).report(out);
|
HANDLER(gain).report(out);
|
||||||
|
HANDLER(group_by_).report(out);
|
||||||
|
HANDLER(group_title_format_).report(out);
|
||||||
HANDLER(head_).report(out);
|
HANDLER(head_).report(out);
|
||||||
HANDLER(invert).report(out);
|
HANDLER(invert).report(out);
|
||||||
HANDLER(limit_).report(out);
|
HANDLER(limit_).report(out);
|
||||||
|
|
@ -596,6 +598,22 @@ public:
|
||||||
" - get_at(total_expr, 1)");
|
" - get_at(total_expr, 1)");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
OPTION__
|
||||||
|
(report_t, group_by_,
|
||||||
|
expr_t expr;
|
||||||
|
CTOR(report_t, group_by_) {}
|
||||||
|
void set_expr(const optional<string>& whence, const string& str) {
|
||||||
|
expr = str;
|
||||||
|
on(whence, str);
|
||||||
|
}
|
||||||
|
DO_(args) {
|
||||||
|
set_expr(args[0].to_string(), args[1].to_string());
|
||||||
|
});
|
||||||
|
|
||||||
|
OPTION__(report_t, group_title_format_, CTOR(report_t, group_title_format_) {
|
||||||
|
on(none, "%(value)\n");
|
||||||
|
});
|
||||||
|
|
||||||
OPTION(report_t, head_);
|
OPTION(report_t, head_);
|
||||||
|
|
||||||
OPTION_(report_t, invert, DO() {
|
OPTION_(report_t, invert, DO() {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue