/* * Copyright (c) 2003-2009, 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 * * @brief Brief * * Long. */ #ifndef _REPORT_H #define _REPORT_H #include "session.h" #include "chain.h" namespace ledger { // 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. Transaction or commodity iteration. In this mode, all the journal's // entries, the transactions of a specific entry, or all the journal's // commodities are walked. In the first two cases, it's the underlying // transactions 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 // transaction 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 transactions of an entry: walk_transactions. // b. The entries of a journal: walk_entries. // 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. /** * @brief Brief * * Long. */ 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 uint_least8_t budget_flags; explicit report_t(session_t& _session); virtual ~report_t() { output_stream.close(); } void xacts_report(xact_handler_ptr handler); void entry_report(xact_handler_ptr handler, entry_t& entry); void accounts_report(acct_handler_ptr handler); void commodities_report(xact_handler_ptr handler); void sum_all_accounts(); 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_value(call_scope_t& scope); value_t fn_print_balance(call_scope_t& scope); value_t fn_strip(call_scope_t& scope); value_t fn_truncate(call_scope_t& scope); value_t fn_quoted(call_scope_t& scope); value_t fn_join(call_scope_t& scope); value_t fn_options(call_scope_t& scope) { return value_t(static_cast(this)); } void append_predicate(const string& str) { if (HANDLED(limit_)) HANDLER(limit_).on(string("(") + HANDLER(limit_).str() + ")&" + str); else HANDLER(limit_).on(str); } void append_secondary_predicate(const string& str) { if (HANDLED(only_)) HANDLER(only_).on(string("(") + HANDLER(only_).str() + ")&" + str); else HANDLER(only_).on(str); } void append_display_predicate(const string& str) { if (HANDLED(display_)) HANDLER(display_).on(string("(") + HANDLER(display_).str() + ")&" + str); else HANDLER(display_).on(str); } keep_details_t what_to_keep() { return keep_details_t(HANDLED(lots) || HANDLED(lot_prices), HANDLED(lots) || HANDLED(lot_dates), HANDLED(lots) || HANDLED(lot_tags), HANDLED(base)); } option_t * lookup_option(const char * p); virtual void define(const string& name, expr_t::ptr_op_t def) { session.define(name, def); } virtual expr_t::ptr_op_t lookup(const string& name); /** * Option handlers */ OPTION(report_t, abbrev_len_); OPTION(report_t, account_); OPTION(report_t, actual); // -L OPTION(report_t, add_budget); OPTION__ (report_t, amount_, // -t expr_t expr; CTOR(report_t, amount_) { expr = "amount"; on("amount"); } DO_(args) { expr = args[0].to_string(); }); OPTION(report_t, amount_data); // -j OPTION(report_t, anon); OPTION(report_t, ansi); OPTION(report_t, ansi_invert); OPTION(report_t, average); // -A OPTION(report_t, balance_format_); OPTION(report_t, base); OPTION_(report_t, basis, DO() { // -B parent->HANDLER(revalued).off(); parent->HANDLER(amount_).on("cost"); parent->HANDLER(total_).on("total_cost"); }); OPTION_(report_t, begin_, DO_(args) { // -b interval_t interval(args[0].to_string()); if (! is_valid(interval.begin)) throw_(std::invalid_argument, "Could not determine beginning of period '" << args[0].to_string() << "'"); string predicate = "date>=[" + to_iso_extended_string(interval.begin) + "]"; parent->append_predicate(predicate); }); OPTION(report_t, budget); OPTION(report_t, by_payee); // -P OPTION(report_t, cache_); OPTION(report_t, cleared); // -C OPTION(report_t, code_as_payee); OPTION_(report_t, collapse, DO() { // -n // Make sure that balance reports are collapsed too parent->append_display_predicate("depth<=1"); }); OPTION_(report_t, collapse_if_zero, DO() { parent->HANDLER(collapse).on(); }); OPTION(report_t, comm_as_payee); // -x OPTION(report_t, cost); OPTION(report_t, csv_format_); OPTION(report_t, current); // -c OPTION(report_t, daily); OPTION(report_t, date_format_); // -y OPTION(report_t, deviation); // -D OPTION_(report_t, display_, DO_(args) { // -d parent->append_display_predicate(args[0].to_string()); }); OPTION__ (report_t, display_amount_, expr_t expr; CTOR(report_t, display_amount_) { expr = "amount_expr"; on("amount_expr"); } DO_(args) { expr = args[0].to_string(); }); OPTION__ (report_t, display_total_, expr_t expr; CTOR(report_t, display_total_) { expr = "total_expr"; on("total_expr"); } DO_(args) { expr = args[0].to_string(); }); OPTION(report_t, dow); OPTION(report_t, effective); OPTION(report_t, empty); // -E OPTION_(report_t, end_, DO_(args) { // -e interval_t interval(args[0].to_string()); if (! is_valid(interval.begin)) throw_(std::invalid_argument, "Could not determine end of period '" << args[0].to_string() << "'"); string predicate = "date<[" + to_iso_extended_string(interval.begin) + "]"; parent->append_predicate(predicate); #if 0 terminus = interval.begin; #endif }); OPTION(report_t, equity_format_); OPTION(report_t, forecast_); OPTION(report_t, format_); // -F OPTION(report_t, gain); // -G OPTION(report_t, head_); OPTION(report_t, invert); OPTION_(report_t, limit_, DO_(args) { // -l parent->append_predicate(args[0].to_string()); }); OPTION(report_t, lot_dates); OPTION(report_t, lot_prices); OPTION(report_t, lot_tags); OPTION(report_t, lots); OPTION_(report_t, market, DO() { // -V parent->HANDLER(revalued).on(); parent->HANDLER(display_amount_).on("market_value(amount_expr)"); parent->HANDLER(display_total_).on("market_value(total_expr)"); }); OPTION(report_t, monthly); // -M OPTION_(report_t, only_, DO_(args) { // -d parent->append_secondary_predicate(args[0].to_string()); }); OPTION(report_t, output_); // -o OPTION(report_t, pager_); OPTION(report_t, pending); OPTION(report_t, percentage); // -% OPTION(report_t, performance); // -g OPTION(report_t, period_); // -p OPTION(report_t, period_sort_); OPTION(report_t, plot_amount_format_); OPTION(report_t, plot_total_format_); OPTION_(report_t, price, DO() { // -I parent->HANDLER(revalued).off(); parent->HANDLER(amount_).on("price"); parent->HANDLER(total_).on("total_price"); }); OPTION(report_t, price_exp_); // -Z OPTION(report_t, prices_format_); OPTION(report_t, pricesdb_format_); OPTION(report_t, print_format_); OPTION_(report_t, quantity, DO() { // -O parent->HANDLER(revalued).off(); parent->HANDLER(amount_).on("amount"); parent->HANDLER(total_).on("total"); }); OPTION(report_t, quarterly); OPTION(report_t, real); // -R OPTION(report_t, register_format_); OPTION(report_t, related); // -r OPTION(report_t, related_all); OPTION(report_t, revalued); OPTION(report_t, revalued_only); OPTION(report_t, set_price_); OPTION_(report_t, sort_, DO_(args) { // -S on(args[0].to_string()); parent->HANDLER(sort_entries_).off(); parent->HANDLER(sort_all_).off(); }); OPTION_(report_t, sort_all_, DO_(args) { parent->HANDLER(sort_).on(args[0].to_string()); parent->HANDLER(sort_entries_).off(); }); OPTION_(report_t, sort_entries_, DO_(args) { parent->HANDLER(sort_).on(args[0].to_string()); parent->HANDLER(sort_all_).off(); }); OPTION(report_t, subtotal); // -s OPTION(report_t, tail_); OPTION__ (report_t, total_, // -T expr_t expr; CTOR(report_t, total_) { expr = "total"; on("total"); } DO_(args) { expr = args[0].to_string(); }); OPTION(report_t, total_data); // -J OPTION(report_t, totals); OPTION(report_t, truncate_); OPTION(report_t, unbudgeted); OPTION(report_t, uncleared); // -U OPTION(report_t, weekly); // -W OPTION(report_t, wide); // -w OPTION(report_t, wide_register_format_); OPTION(report_t, yearly); // -Y }; // jww (2009-02-10): These should perhaps live elsewhere value_t entry_command(call_scope_t& args); value_t template_command(call_scope_t& args); entry_t * derive_new_entry(report_t& report, value_t::sequence_t::const_iterator i, value_t::sequence_t::const_iterator end); string join_args(call_scope_t& args); } // namespace ledger #endif // _REPORT_H