ledger/src/report.h
John Wiegley dbac09405f Added new command: "pricemap [DATE]"
This outputs the pricing relationship of commodities in your data file,
as of DATE (optional), using the DOT language.  If you have graphviz
installed, it can be viewed quite simply using:

  ledger pricemap | dotty -

Each relationship in the graph shows the conversion factor to exchange
one commodity for another, and the date at which this factor was
determined.
2010-06-04 02:53:18 -04:00

986 lines
31 KiB
C++

/*
* Copyright (c) 2003-2010, John Wiegley. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of New Artisans LLC nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @addtogroup report
*/
/**
* @file report.h
* @author John Wiegley
*
* @ingroup report
*/
#ifndef _REPORT_H
#define _REPORT_H
#include "interactive.h"
#include "expr.h"
#include "query.h"
#include "chain.h"
#include "stream.h"
#include "option.h"
#include "commodity.h"
#include "annotate.h"
#include "session.h"
#include "format.h"
namespace ledger {
class session_t;
class xact_t;
// These are the elements of any report:
//
// 1. Formatting string used for outputting the underlying ReportedType.
//
// 2. Handler object for the ReportedType. This is constructed using #1, or
// else #1 is ignored completely. This handler object is also constructed
// with the output stream that will be used during formatting.
//
// --- The details of #1 and #2 together represent the ItemHandler.
//
// 3. Mode of the report. Currently there are four modes:
//
// a. Posting or commodity iteration. In this mode, all the journal's
// xacts, the postings of a specific xact, or all the journal's
// commodities are walked. In the first two cases, it's the underlying
// postings which are passed to #2; in the second case, each
// commodity is passed to #2.
//
// b. Account iteration. This employs step 'a', but add a prologue and
// epilogue to it. In the prologue it "sums" all account totals and
// subtotals; in the epilogue it calls yet another handler whose job is
// reporting (the handler used in 'a' is only for calculation).
//
// There is one variation on 'b' in which a "totals" line is also
// displayed.
//
// c. Write journal. In this mode, a single function is called that output
// the journal object as a textual file. #2 is used to print out each
// posting in the journal.
//
// d. Dump binary file. This is just like 'c', except that it dumps out a
// binary file and #2 is completely ignored.
//
// 4. For 'a' and 'b' in #3, there is a different iteration function called,
// depending on whether we're iterating:
//
// a. The postings of an xact: walk_postings.
// b. The xacts of a journal: walk_xacts.
// c. The commodities of a journal: walk_commodities.
//
// 5. Finally, for the 'a' and 'b' reporting modes, there is a variant which
// says that the formatter should be "flushed" after the entities are
// iterated. This does not happen for the commodities iteration, however.
class report_t : public scope_t
{
report_t();
public:
session_t& session;
output_stream_t output_stream;
#define BUDGET_NO_BUDGET 0x00
#define BUDGET_BUDGETED 0x01
#define BUDGET_UNBUDGETED 0x02
#define BUDGET_WRAP_VALUES 0x04
datetime_t terminus;
uint_least8_t budget_flags;
explicit report_t(session_t& _session)
: session(_session), terminus(CURRENT_TIME()),
budget_flags(BUDGET_NO_BUDGET) {}
virtual ~report_t() {
output_stream.close();
}
void normalize_options(const string& verb);
void parse_query_args(const value_t& args, const string& whence);
void posts_report(post_handler_ptr handler);
void generate_report(post_handler_ptr handler);
void xact_report(post_handler_ptr handler, xact_t& xact);
void accounts_report(acct_handler_ptr handler);
void commodities_report(post_handler_ptr handler);
value_t fn_amount_expr(call_scope_t& scope);
value_t fn_total_expr(call_scope_t& scope);
value_t fn_display_amount(call_scope_t& scope);
value_t fn_display_total(call_scope_t& scope);
value_t fn_market(call_scope_t& scope);
value_t fn_get_at(call_scope_t& scope);
value_t fn_is_seq(call_scope_t& scope);
value_t fn_strip(call_scope_t& scope);
value_t fn_trim(call_scope_t& scope);
value_t fn_print(call_scope_t& scope);
value_t scrub(value_t val);
value_t fn_scrub(call_scope_t& scope);
value_t fn_quantity(call_scope_t& scope);
value_t fn_rounded(call_scope_t& scope);
value_t fn_unrounded(call_scope_t& scope);
value_t fn_truncated(call_scope_t& scope);
value_t fn_floor(call_scope_t& scope);
value_t fn_abs(call_scope_t& scope);
value_t fn_justify(call_scope_t& scope);
value_t fn_quoted(call_scope_t& scope);
value_t fn_join(call_scope_t& scope);
value_t fn_format_date(call_scope_t& scope);
value_t fn_ansify_if(call_scope_t& scope);
value_t fn_percent(call_scope_t& scope);
value_t fn_price(call_scope_t& scope);
value_t fn_lot_date(call_scope_t& scope);
value_t fn_lot_price(call_scope_t& scope);
value_t fn_lot_tag(call_scope_t& scope);
value_t fn_to_boolean(call_scope_t& scope);
value_t fn_to_int(call_scope_t& scope);
value_t fn_to_datetime(call_scope_t& scope);
value_t fn_to_date(call_scope_t& scope);
value_t fn_to_amount(call_scope_t& scope);
value_t fn_to_balance(call_scope_t& scope);
value_t fn_to_string(call_scope_t& scope);
value_t fn_to_mask(call_scope_t& scope);
value_t fn_to_sequence(call_scope_t& scope);
value_t fn_now(call_scope_t&) {
return terminus;
}
value_t fn_today(call_scope_t&) {
return terminus.date();
}
value_t fn_options(call_scope_t&) {
return value_t(static_cast<scope_t *>(this));
}
string report_format(option_t<report_t>& option) {
if (HANDLED(format_))
return HANDLER(format_).str();
return option.str();
}
optional<string> maybe_format(option_t<report_t>& option) {
if (option)
return option.str();
return none;
}
value_t reload_command(call_scope_t&);
value_t echo_command(call_scope_t& scope);
value_t pricemap_command(call_scope_t& scope);
keep_details_t what_to_keep() {
bool lots = HANDLED(lots) || HANDLED(lots_actual);
return keep_details_t(lots || HANDLED(lot_prices),
lots || HANDLED(lot_dates),
lots || HANDLED(lot_tags),
HANDLED(lots_actual));
}
void report_options(std::ostream& out)
{
HANDLER(abbrev_len_).report(out);
HANDLER(account_).report(out);
HANDLER(actual).report(out);
HANDLER(actual_dates).report(out);
HANDLER(add_budget).report(out);
HANDLER(amount_).report(out);
HANDLER(amount_data).report(out);
HANDLER(anon).report(out);
HANDLER(average).report(out);
HANDLER(balance_format_).report(out);
HANDLER(base).report(out);
HANDLER(basis).report(out);
HANDLER(begin_).report(out);
HANDLER(budget).report(out);
HANDLER(budget_format_).report(out);
HANDLER(by_payee).report(out);
HANDLER(cleared).report(out);
HANDLER(cleared_format_).report(out);
HANDLER(color).report(out);
HANDLER(collapse).report(out);
HANDLER(collapse_if_zero).report(out);
HANDLER(columns_).report(out);
HANDLER(csv_format_).report(out);
HANDLER(current).report(out);
HANDLER(daily).report(out);
HANDLER(date_).report(out);
HANDLER(date_format_).report(out);
HANDLER(datetime_format_).report(out);
HANDLER(depth_).report(out);
HANDLER(deviation).report(out);
HANDLER(display_).report(out);
HANDLER(display_amount_).report(out);
HANDLER(display_total_).report(out);
HANDLER(dow).report(out);
HANDLER(effective).report(out);
HANDLER(empty).report(out);
HANDLER(end_).report(out);
HANDLER(equity).report(out);
HANDLER(exact).report(out);
HANDLER(exchange_).report(out);
HANDLER(flat).report(out);
HANDLER(force_color).report(out);
HANDLER(force_pager).report(out);
HANDLER(forecast_while_).report(out);
HANDLER(forecast_years_).report(out);
HANDLER(format_).report(out);
HANDLER(gain).report(out);
HANDLER(group_by_).report(out);
HANDLER(group_title_format_).report(out);
HANDLER(head_).report(out);
HANDLER(invert).report(out);
HANDLER(limit_).report(out);
HANDLER(lot_dates).report(out);
HANDLER(lot_prices).report(out);
HANDLER(lot_tags).report(out);
HANDLER(lots).report(out);
HANDLER(lots_actual).report(out);
HANDLER(market).report(out);
HANDLER(meta_).report(out);
HANDLER(monthly).report(out);
HANDLER(no_rounding).report(out);
HANDLER(no_titles).report(out);
HANDLER(no_total).report(out);
HANDLER(now_).report(out);
HANDLER(only_).report(out);
HANDLER(output_).report(out);
HANDLER(pager_).report(out);
HANDLER(payee_).report(out);
HANDLER(pending).report(out);
HANDLER(percent).report(out);
HANDLER(period_).report(out);
HANDLER(pivot_).report(out);
HANDLER(plot_amount_format_).report(out);
HANDLER(plot_total_format_).report(out);
HANDLER(prepend_format_).report(out);
HANDLER(prepend_width_).report(out);
HANDLER(price).report(out);
HANDLER(prices_format_).report(out);
HANDLER(pricedb_format_).report(out);
HANDLER(print_virtual).report(out);
HANDLER(quantity).report(out);
HANDLER(quarterly).report(out);
HANDLER(raw).report(out);
HANDLER(real).report(out);
HANDLER(register_format_).report(out);
HANDLER(related).report(out);
HANDLER(related_all).report(out);
HANDLER(revalued).report(out);
HANDLER(revalued_only).report(out);
HANDLER(revalued_total_).report(out);
HANDLER(seed_).report(out);
HANDLER(sort_).report(out);
HANDLER(sort_all_).report(out);
HANDLER(sort_xacts_).report(out);
HANDLER(start_of_week_).report(out);
HANDLER(subtotal).report(out);
HANDLER(tail_).report(out);
HANDLER(total_).report(out);
HANDLER(total_data).report(out);
HANDLER(truncate_).report(out);
HANDLER(unbudgeted).report(out);
HANDLER(uncleared).report(out);
HANDLER(unrealized).report(out);
HANDLER(unrealized_gains_).report(out);
HANDLER(unrealized_losses_).report(out);
HANDLER(unround).report(out);
HANDLER(unsorted).report(out);
HANDLER(weekly).report(out);
HANDLER(wide).report(out);
HANDLER(yearly).report(out);
HANDLER(meta_width_).report(out);
HANDLER(date_width_).report(out);
HANDLER(payee_width_).report(out);
HANDLER(account_width_).report(out);
HANDLER(amount_width_).report(out);
HANDLER(total_width_).report(out);
}
option_t<report_t> * lookup_option(const char * p);
virtual void define(const symbol_t::kind_t kind, const string& name,
expr_t::ptr_op_t def);
virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind,
const string& name);
/**
* Option handlers
*/
OPTION__(report_t, abbrev_len_,
CTOR(report_t, abbrev_len_) { on_with(none, 2L); });
OPTION(report_t, account_);
OPTION_(report_t, actual, DO() { // -L
parent->HANDLER(limit_).on(string("--actual"), "actual");
});
OPTION(report_t, actual_dates);
OPTION_(report_t, add_budget, DO() {
parent->budget_flags |= BUDGET_BUDGETED | BUDGET_UNBUDGETED;
});
OPTION__
(report_t, amount_, // -t
expr_t expr;
CTOR(report_t, amount_) {
set_expr(none, "amount");
}
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, amount_data); // -j
OPTION(report_t, anon);
OPTION_(report_t, average, DO() { // -A
parent->HANDLER(display_total_)
.set_expr(string("--average"), "count>0?(total_expr/count):0");
});
OPTION__(report_t, balance_format_, CTOR(report_t, balance_format_) {
on(none,
"%(justify(scrub(display_total), 20, 20 + prepend_width, true, color))"
" %(!options.flat ? depth_spacer : \"\")"
"%-(ansify_if(partial_account(options.flat), blue if color))\n%/"
"%$1\n%/"
"--------------------\n");
});
OPTION(report_t, base);
OPTION_(report_t, basis, DO() { // -B
parent->HANDLER(revalued).on_only(string("--basis"));
parent->HANDLER(amount_).set_expr(string("--basis"), "rounded(cost)");
});
OPTION_(report_t, begin_, DO_(args) { // -b
date_interval_t interval(args[1].to_string());
optional<date_t> begin = interval.begin(parent->session.current_year);
if (! begin)
throw_(std::invalid_argument,
_("Could not determine beginning of period '%1'")
<< args[1].to_string());
string predicate = "date>=[" + to_iso_extended_string(*begin) + "]";
parent->HANDLER(limit_).on(string("--begin"), predicate);
});
OPTION_(report_t, budget, DO() {
parent->budget_flags |= BUDGET_BUDGETED;
});
OPTION__(report_t, budget_format_, CTOR(report_t, budget_format_) {
on(none,
"%(justify(scrub(get_at(total_expr, 0)), 12, -1, true, color))"
" %(justify(scrub(- get_at(total_expr, 1)), 12, "
" 12 + 1 + 12, true, color))"
" %(justify(scrub(get_at(total_expr, 1) + "
" get_at(total_expr, 0)), 12, "
" 12 + 1 + 12 + 1 + 12, true, color))"
" %(ansify_if("
" justify((get_at(total_expr, 1) ? "
" scrub((100% * get_at(total_expr, 0)) / "
" - get_at(total_expr, 1)) : 0), "
" 5, -1, true, false),"
" magenta if (color and get_at(total_expr, 1) and "
" (abs(quantity(get_at(total_expr, 0)) / "
" quantity(get_at(total_expr, 1))) >= 1))))"
" %(!options.flat ? depth_spacer : \"\")"
"%-(ansify_if(partial_account(options.flat), blue if color))\n"
"%/%$1 %$2 %$3 %$4\n%/"
"------------ ------------ ------------ -----\n");
});
OPTION(report_t, by_payee); // -P
OPTION_(report_t, cleared, DO() { // -C
parent->HANDLER(limit_).on(string("--cleared"), "cleared");
});
OPTION__(report_t, cleared_format_, CTOR(report_t, cleared_format_) {
on(none,
"%(justify(scrub(get_at(total_expr, 0)), 16, 16 + prepend_width, "
" true, color)) %(justify(scrub(get_at(total_expr, 1)), 18, "
" 36 + prepend_width, true, color))"
" %(latest_cleared ? format_date(latest_cleared) : \" \")"
" %(!options.flat ? depth_spacer : \"\")"
"%-(ansify_if(partial_account(options.flat), blue if color))\n%/"
"%$1 %$2 %$3\n%/"
"---------------- ---------------- ---------\n");
});
OPTION(report_t, color);
OPTION_(report_t, collapse, DO() { // -n
// Make sure that balance reports are collapsed too, but only apply it
// to account xacts
parent->HANDLER(display_).on(string("--collapse"), "post|depth<=1");
});
OPTION_(report_t, collapse_if_zero, DO() {
parent->HANDLER(collapse).on_only(string("--collapse-if-zero"));
});
OPTION(report_t, columns_);
OPTION(report_t, count);
OPTION__(report_t, csv_format_, CTOR(report_t, csv_format_) {
on(none,
"%(quoted(date)),"
"%(quoted(code)),"
"%(quoted(payee)),"
"%(quoted(display_account)),"
"%(quoted(commodity)),"
"%(quoted(quantity(scrub(display_amount)))),"
"%(quoted(cleared ? \"*\" : (pending ? \"!\" : \"\"))),"
"%(quoted(join(note | xact.note)))\n");
});
OPTION_(report_t, current, DO() { // -c
parent->HANDLER(limit_).on(string("--current"), "date<=today");
});
OPTION_(report_t, daily, DO() { // -D
parent->HANDLER(period_).on(string("--daily"), "daily");
});
OPTION(report_t, date_);
OPTION(report_t, date_format_);
OPTION(report_t, datetime_format_);
OPTION_(report_t, depth_, DO_(scope) {
interactive_t args(scope, "sl");
parent->HANDLER(display_).on(string("--depth"),
string("depth<=") + args.get<string>(1));
});
OPTION_(report_t, deviation, DO() {
parent->HANDLER(display_total_)
.set_expr(string("--deviation"), "amount_expr-total_expr/count");
});
OPTION__
(report_t, display_, // -d
CTOR(report_t, display_) {}
virtual void on_with(const optional<string>& whence, const value_t& text) {
if (! handled)
option_t<report_t>::on_with(whence, text);
else
option_t<report_t>::on_with(whence,
string_value(string("(") + str() + ")&(" +
text.as_string() + ")"));
});
OPTION__
(report_t, display_amount_,
expr_t expr;
CTOR(report_t, display_amount_) {
set_expr(none, "amount_expr");
}
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, display_total_,
expr_t expr;
CTOR(report_t, display_total_) {
set_expr(none, "total_expr");
}
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, dow);
OPTION(report_t, effective);
OPTION(report_t, empty); // -E
OPTION_(report_t, end_, DO_(args) { // -e
date_interval_t interval(args[1].to_string());
// Use begin() here so that if the user says --end=2008, we end on
// 2008/01/01 instead of 2009/01/01 (which is what end() would return).
optional<date_t> end = interval.begin(parent->session.current_year);
if (! end)
throw_(std::invalid_argument,
_("Could not determine end of period '%1'")
<< args[1].to_string());
string predicate = "date<[" + to_iso_extended_string(*end) + "]";
parent->HANDLER(limit_).on(string("--end"), predicate);
parent->terminus = datetime_t(*end);
});
OPTION(report_t, equity);
OPTION(report_t, exact);
OPTION_(report_t, exchange_, DO_(args) { // -X
on_with(args[0].as_string(), args[1]);
call_scope_t no_args(*parent);
no_args.push_back(args[0]);
parent->HANDLER(market).parent = parent;
parent->HANDLER(market).handler(no_args);
});
OPTION(report_t, flat);
OPTION(report_t, force_color);
OPTION(report_t, force_pager);
OPTION(report_t, forecast_while_);
OPTION(report_t, forecast_years_);
OPTION(report_t, format_); // -F
OPTION_(report_t, gain, DO() { // -G
parent->HANDLER(revalued).on_only(string("--gain"));
parent->HANDLER(amount_).set_expr(string("--gain"), "(amount, cost)");
// Since we are displaying the amounts of revalued postings, they
// will end up being composite totals, and hence a pair of pairs.
parent->HANDLER(display_amount_)
.set_expr(string("--gain"),
"use_direct_amount ? amount :"
" (is_seq(get_at(amount_expr, 0)) ?"
" get_at(get_at(amount_expr, 0), 0) :"
" market(get_at(amount_expr, 0), date, exchange)"
" - get_at(amount_expr, 1))");
parent->HANDLER(revalued_total_)
.set_expr(string("--gain"),
"(market(get_at(total_expr, 0), date, exchange), "
"get_at(total_expr, 1))");
parent->HANDLER(display_total_)
.set_expr(string("--gain"),
"use_direct_amount ? total_expr :"
" market(get_at(total_expr, 0), date, exchange)"
" - 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, invert, DO() {
parent->HANDLER(amount_).set_expr(string("--invert"), "-amount");
});
OPTION__
(report_t, limit_, // -l
CTOR(report_t, limit_) {}
virtual void on_with(const optional<string>& whence, const value_t& text) {
if (! handled)
option_t<report_t>::on_with(whence, text);
else
option_t<report_t>::on_with(whence,
string_value(string("(") + str() + ")&(" +
text.as_string() + ")"));
});
OPTION(report_t, lot_dates);
OPTION(report_t, lot_prices);
OPTION(report_t, lot_tags);
OPTION(report_t, lots);
OPTION(report_t, lots_actual);
OPTION_(report_t, market, DO() { // -V
parent->HANDLER(revalued).on_only(string("--market"));
parent->HANDLER(display_amount_)
.set_expr(string("--market"), "market(amount_expr, date, exchange)");
parent->HANDLER(display_total_)
.set_expr(string("--market"), "market(total_expr, date, exchange)");
});
OPTION(report_t, meta_);
OPTION_(report_t, monthly, DO() { // -M
parent->HANDLER(period_).on(string("--monthly"), "monthly");
});
OPTION_(report_t, no_color, DO() {
parent->HANDLER(color).off();
});
OPTION(report_t, no_rounding);
OPTION(report_t, no_titles);
OPTION(report_t, no_total);
OPTION_(report_t, now_, DO_(args) {
date_interval_t interval(args[1].to_string());
optional<date_t> begin = interval.begin(parent->session.current_year);
if (! begin)
throw_(std::invalid_argument,
_("Could not determine beginning of period '%1'")
<< args[1].to_string());
ledger::epoch = parent->terminus = datetime_t(*begin);
parent->session.current_year = ledger::epoch->date().year();
});
OPTION__
(report_t, only_,
CTOR(report_t, only_) {}
virtual void on_with(const optional<string>& whence, const value_t& text) {
if (! handled)
option_t<report_t>::on_with(whence, text);
else
option_t<report_t>::on_with(whence,
string_value(string("(") + str() + ")&(" +
text.as_string() + ")"));
});
OPTION(report_t, output_); // -o
#ifdef HAVE_ISATTY
OPTION__
(report_t, pager_,
CTOR(report_t, pager_) {
if (! std::getenv("PAGER") && isatty(STDOUT_FILENO)) {
bool have_less = false;
if (exists(path("/opt/local/bin/less")) ||
exists(path("/usr/local/bin/less")) ||
exists(path("/usr/bin/less")))
have_less = true;
if (have_less) {
on(none, "less");
setenv("LESS", "-FRSX", 0); // don't overwrite
}
}
}
virtual void on_with(const optional<string>& whence, const value_t& text) {
string cmd(text.to_string());
if (cmd == "" || cmd == "false" || cmd == "off" ||
cmd == "none" || cmd == "no" || cmd == "disable")
option_t<report_t>::off();
else
option_t<report_t>::on_with(whence, text);
});
#else // HAVE_ISATTY
OPTION__
(report_t, pager_,
CTOR(report_t, pager_) {
}
virtual void on_with(const optional<string>& whence, const value_t& text) {
string cmd(text.to_string());
if (cmd == "" || cmd == "false" || cmd == "off" ||
cmd == "none" || cmd == "no" || cmd == "disable")
option_t<report_t>::off();
else
option_t<report_t>::on_with(whence, text);
});
#endif // HAVE_ISATTY
OPTION(report_t, payee_);
OPTION_(report_t, pending, DO() { // -C
parent->HANDLER(limit_).on(string("--pending"), "pending");
});
OPTION_(report_t, percent, DO() { // -%
parent->HANDLER(total_)
.set_expr(string("--percent"),
"is_account&parent&parent.total&percent(total, parent.total)");
});
OPTION__
(report_t, period_, // -p
CTOR(report_t, period_) {}
virtual void on_with(const optional<string>& whence, const value_t& text) {
if (! handled)
option_t<report_t>::on_with(whence, text);
else
option_t<report_t>::on_with(whence,
string_value(text.as_string() + " " + str()));
});
OPTION(report_t, pivot_);
OPTION__(report_t, plot_amount_format_, CTOR(report_t, plot_amount_format_) {
on(none,
"%(format_date(date, \"%Y-%m-%d\")) %(quantity(scrub(display_amount)))\n");
});
OPTION__(report_t, plot_total_format_, CTOR(report_t, plot_total_format_) {
on(none,
"%(format_date(date, \"%Y-%m-%d\")) %(quantity(scrub(display_total)))\n");
});
OPTION(report_t, prepend_format_);
OPTION_(report_t, prepend_width_, DO_(args) {
value = args[1].to_long();
});
OPTION_(report_t, price, DO() { // -I
parent->HANDLER(display_amount_)
.set_expr(string("--price"), "price(amount_expr)");
parent->HANDLER(display_total_)
.set_expr(string("--price"), "price(total_expr)");
});
OPTION__(report_t, prices_format_, CTOR(report_t, prices_format_) {
on(none,
"%(date) %-8(display_account) %(justify(scrub(display_amount), 12, "
" 2 + 9 + 8 + 12, true, color))\n");
});
OPTION__(report_t, pricedb_format_, CTOR(report_t, pricedb_format_) {
on(none,
"P %(datetime) %(display_account) %(scrub(display_amount))\n");
});
OPTION(report_t, print_virtual);
OPTION_(report_t, quantity, DO() { // -O
parent->HANDLER(revalued).off();
parent->HANDLER(amount_).set_expr(string("--quantity"), "amount");
parent->HANDLER(total_).set_expr(string("--quantity"), "total");
});
OPTION_(report_t, quarterly, DO() {
parent->HANDLER(period_).on(string("--quarterly"), "quarterly");
});
OPTION(report_t, raw);
OPTION_(report_t, real, DO() { // -R
parent->HANDLER(limit_).on(string("--real"), "real");
});
OPTION__(report_t, register_format_, CTOR(report_t, register_format_) {
on(none,
"%(ansify_if(justify(format_date(date), date_width), green "
" if color & date > today))"
" %(ansify_if(justify(truncated(payee, payee_width), payee_width), "
" bold if color & !cleared & actual))"
" %(ansify_if(justify(truncated(display_account, account_width, "
" abbrev_len), account_width), blue if color))"
" %(justify(scrub(display_amount), amount_width, "
" 3 + meta_width + date_width + payee_width + account_width"
" + amount_width + prepend_width, true, color))"
" %(justify(scrub(display_total), total_width, "
" 4 + meta_width + date_width + payee_width + account_width"
" + amount_width + total_width + prepend_width, true, color))\n%/"
"%(justify(\" \", 2 + date_width + payee_width))"
"%$3 %$4 %$5\n");
});
OPTION(report_t, related); // -r
OPTION_(report_t, related_all, DO() {
parent->HANDLER(related).on_only(string("--related-all"));
});
OPTION(report_t, revalued);
OPTION(report_t, revalued_only);
OPTION__
(report_t, revalued_total_,
expr_t expr;
CTOR(report_t, revalued_total_) {}
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, seed_);
OPTION_(report_t, sort_, DO_(args) { // -S
on_with(args[0].as_string(), args[1]);
parent->HANDLER(sort_xacts_).off();
parent->HANDLER(sort_all_).off();
});
OPTION_(report_t, sort_all_, DO_(args) {
parent->HANDLER(sort_).on_with(string("--sort-all"), args[1]);
parent->HANDLER(sort_xacts_).off();
});
OPTION_(report_t, sort_xacts_, DO_(args) {
parent->HANDLER(sort_).on_with(string("--sort-xacts"), args[1]);
parent->HANDLER(sort_all_).off();
});
OPTION(report_t, start_of_week_);
OPTION(report_t, subtotal); // -s
OPTION(report_t, tail_);
OPTION__
(report_t, total_, // -T
expr_t expr;
CTOR(report_t, total_) {
set_expr(none, "total");
}
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, total_data); // -J
OPTION_(report_t, truncate_, DO_(args) {
string style(args[1].to_string());
if (style == "leading")
format_t::default_style = format_t::TRUNCATE_LEADING;
else if (style == "middle")
format_t::default_style = format_t::TRUNCATE_MIDDLE;
else if (style == "trailing")
format_t::default_style = format_t::TRUNCATE_TRAILING;
else
throw_(std::invalid_argument,
_("Unrecognized truncation style: '%1'") << style);
format_t::default_style_changed = true;
});
OPTION_(report_t, unbudgeted, DO() {
parent->budget_flags |= BUDGET_UNBUDGETED;
});
OPTION_(report_t, uncleared, DO() { // -U
parent->HANDLER(limit_).on(string("--uncleared"), "uncleared|pending");
});
OPTION(report_t, unrealized);
OPTION(report_t, unrealized_gains_);
OPTION(report_t, unrealized_losses_);
OPTION_(report_t, unround, DO() {
parent->HANDLER(display_amount_)
.set_expr(string("--unround"), "unrounded(amount_expr)");
parent->HANDLER(display_total_)
.set_expr(string("--unround"), "unrounded(total_expr)");
});
OPTION(report_t, unsorted);
OPTION_(report_t, weekly, DO() { // -W
parent->HANDLER(period_).on(string("--weekly"), "weekly");
});
OPTION_(report_t, wide, DO() { // -w
parent->HANDLER(columns_).on_with(string("--wide"), 132L);
});
OPTION_(report_t, yearly, DO() { // -Y
parent->HANDLER(period_).on(string("--yearly"), "yearly");
});
OPTION__(report_t, meta_width_,
bool specified;
CTOR(report_t, meta_width_) { specified = false; }
DO_(args) { value = args[1].to_long(); specified = true; });
OPTION__(report_t, date_width_,
bool specified;
CTOR(report_t, date_width_) { specified = false; }
DO_(args) { value = args[1].to_long(); specified = true; });
OPTION__(report_t, payee_width_,
bool specified;
CTOR(report_t, payee_width_) { specified = false; }
DO_(args) { value = args[1].to_long(); specified = true; });
OPTION__(report_t, account_width_,
bool specified;
CTOR(report_t, account_width_) { specified = false; }
DO_(args) { value = args[1].to_long(); specified = true; });
OPTION__(report_t, amount_width_,
bool specified;
CTOR(report_t, amount_width_) { specified = false; }
DO_(args) { value = args[1].to_long(); specified = true; });
OPTION__(report_t, total_width_,
bool specified;
CTOR(report_t, total_width_) { specified = false; }
DO_(args) { value = args[1].to_long(); specified = true; });
};
template <class Type = post_t,
class handler_ptr = post_handler_ptr,
void (report_t::*report_method)(handler_ptr) =
&report_t::posts_report>
class reporter
{
shared_ptr<item_handler<Type> > handler;
report_t& report;
string whence;
public:
reporter(item_handler<Type> * _handler,
report_t& _report, const string& _whence)
: handler(_handler), report(_report), whence(_whence) {}
value_t operator()(call_scope_t& args)
{
if (args.size() > 0)
report.parse_query_args(args.value(), whence);
(report.*report_method)(handler_ptr(handler));
return true;
}
};
} // namespace ledger
#endif // _REPORT_H