diff options
Diffstat (limited to 'report.cc')
-rw-r--r-- | report.cc | 264 |
1 files changed, 258 insertions, 6 deletions
@@ -30,20 +30,272 @@ */ #include "report.h" +#include "reconcile.h" namespace ledger { -void report_t::apply_transforms(expr::scope_t& scope) +xact_handler_ptr +report_t::chain_xact_handlers(xact_handler_ptr base_handler, + const bool handle_individual_transactions) { + bool remember_components = false; + + xact_handler_ptr handler(base_handler); + + // format_transactions write each transaction received to the + // output stream. + if (handle_individual_transactions) { + // truncate_entries cuts off a certain number of _entries_ from + // being displayed. It does not affect calculation. + if (head_entries || tail_entries) + handler.reset(new truncate_entries(handler, head_entries, tail_entries)); + + // filter_transactions will only pass through transactions + // matching the `display_predicate'. + if (! display_predicate.empty()) + handler.reset(new filter_transactions(handler, display_predicate)); + + // calc_transactions computes the running total. When this + // appears will determine, for example, whether filtered + // transactions are included or excluded from the running total. + handler.reset(new calc_transactions(handler)); + + // component_transactions looks for reported transaction that + // match the given `descend_expr', and then reports the + // transactions which made up the total for that reported + // transaction. + if (! descend_expr.empty()) { + std::list<std::string> descend_exprs; + + std::string::size_type beg = 0; + for (std::string::size_type pos = descend_expr.find(';'); + pos != std::string::npos; + beg = pos + 1, pos = descend_expr.find(';', beg)) + descend_exprs.push_back(std::string(descend_expr, beg, pos - beg)); + descend_exprs.push_back(std::string(descend_expr, beg)); + + for (std::list<std::string>::reverse_iterator i = + descend_exprs.rbegin(); + i != descend_exprs.rend(); + i++) + handler.reset(new component_transactions(handler, *i)); + + remember_components = true; + } + + // reconcile_transactions will pass through only those + // transactions which can be reconciled to a given balance + // (calculated against the transactions which it receives). + if (! reconcile_balance.empty()) { + datetime_t cutoff = current_moment; + if (! reconcile_date.empty()) + cutoff = parse_datetime(reconcile_date); + handler.reset(new reconcile_transactions + (handler, value_t(reconcile_balance), cutoff)); + } + + // filter_transactions will only pass through transactions + // matching the `secondary_predicate'. + if (! secondary_predicate.empty()) + handler.reset(new filter_transactions(handler, secondary_predicate)); + + // sort_transactions will sort all the transactions it sees, based + // on the `sort_order' value expression. + if (! sort_string.empty()) { + if (entry_sort) + handler.reset(new sort_entries(handler, sort_string)); + else + handler.reset(new sort_transactions(handler, sort_string)); + } + + // changed_value_transactions adds virtual transactions to the + // list to account for changes in market value of commodities, + // which otherwise would affect the running total unpredictably. + if (show_revalued) + handler.reset(new changed_value_transactions(handler, show_revalued_only)); + + // collapse_transactions causes entries with multiple transactions + // to appear as entries with a subtotaled transaction for each + // commodity used. + if (show_collapsed) + handler.reset(new collapse_transactions(handler)); + + // subtotal_transactions combines all the transactions it receives + // into one subtotal entry, which has one transaction for each + // commodity in each account. + // + // period_transactions is like subtotal_transactions, but it + // subtotals according to time periods rather than totalling + // everything. + // + // dow_transactions is like period_transactions, except that it + // reports all the transactions that fall on each subsequent day + // of the week. + if (show_subtotal) + handler.reset(new subtotal_transactions(handler, remember_components)); + + if (days_of_the_week) + handler.reset(new dow_transactions(handler, remember_components)); + else if (by_payee) + handler.reset(new by_payee_transactions(handler, remember_components)); + + // interval_transactions groups transactions together based on a + // time period, such as weekly or monthly. + if (! report_period.empty()) { + handler.reset(new interval_transactions(handler, report_period, + remember_components)); + handler.reset(new sort_transactions(handler, "d")); + } + } + + // invert_transactions inverts the value of the transactions it + // receives. + if (show_inverted) + handler.reset(new invert_transactions(handler)); + + // related_transactions will pass along all transactions related + // to the transaction received. If `show_all_related' is true, + // then all the entry's transactions are passed; meaning that if + // one transaction of an entry is to be printed, all the + // transaction for that entry will be printed. + if (show_related) + handler.reset(new related_transactions(handler, show_all_related)); + + // This filter_transactions will only pass through transactions + // matching the `predicate'. + if (! predicate.empty()) + handler.reset(new filter_transactions(handler, predicate)); + #if 0 - typedef tuple<shared_ptr<transform_t>, value_t> transform_details_tuple; + // budget_transactions takes a set of transactions from a data + // file and uses them to generate "budget transactions" which + // balance against the reported transactions. + // + // forecast_transactions is a lot like budget_transactions, except + // that it adds entries only for the future, and does not balance + // them against anything but the future balance. + + if (budget_flags) { + budget_transactions * budget_handler + = new budget_transactions(handler, budget_flags); + budget_handler->add_period_entries(journal->period_entries); + handler.reset(budget_handler; - foreach (transform_details_tuple& transform_details, transforms) { - expr::call_scope_t call_args(scope); - call_args.set_args(transform_details.get<1>()); - (*transform_details.get<0>())(call_args); + // Apply this before the budget handler, so that only matching + // transactions are calculated toward the budget. The use of + // filter_transactions above will further clean the results so + // that no automated transactions that don't match the filter get + // reported. + if (! predicate.empty()) + handler.reset(new filter_transactions(handler, predicate)); + } + else if (! forecast_limit.empty()) { + forecast_transactions * forecast_handler + = new forecast_transactions(handler, forecast_limit); + forecast_handler->add_period_entries(journal->period_entries); + handler.reset(forecast_handler; + + // See above, under budget_transactions. + if (! predicate.empty()) + handler.reset(new filter_transactions(handler, predicate)); } #endif + + if (comm_as_payee) + handler.reset(new set_comm_as_payee(handler)); + else if (code_as_payee) + handler.reset(new set_code_as_payee(handler)); + + return handler; +} + +void report_t::transactions_report(xact_handler_ptr handler) +{ + session_transactions_iterator walker(session); + pass_down_transactions(chain_xact_handlers(handler), walker); + handler->flush(); + + if (DO_VERIFY()) + clean_transactions(); +} + +void report_t::entry_report(xact_handler_ptr handler, entry_t& entry) +{ + entry_transactions_iterator walker(entry); + pass_down_transactions(chain_xact_handlers(handler), walker); + handler->flush(); + + if (DO_VERIFY()) + clean_transactions(entry); +} + +void report_t::sum_all_accounts() +{ + session_transactions_iterator walker(session); + pass_down_transactions + (chain_xact_handlers(xact_handler_ptr(new set_account_value), false), + walker); + // no flush() needed with set_account_value + sum_accounts(*session.master); +} + +void report_t::accounts_report(acct_handler_ptr handler, + const bool print_final_total) +{ + sum_all_accounts(); + + if (sort_string.empty()) { + accounts_iterator walker(*session.master); + pass_down_accounts<accounts_iterator>(handler, walker); + } else { + sorted_accounts_iterator walker(*session.master, sort_string); + pass_down_accounts<sorted_accounts_iterator>(handler, walker); + } + handler->flush(); + + if (print_final_total && account_has_xdata(*session.master)) { + account_xdata_t& xdata = account_xdata(*session.master); + if (! show_collapsed && xdata.total) { +#if 0 + *out << "--------------------\n"; + xdata.value = xdata.total; + handler->format.format(*out, details_t(*journal->master)); +#endif + } + } + + if (DO_VERIFY()) { + clean_transactions(); + clean_accounts(); + } +} + +void report_t::commodities_report(const string& format) +{ +} + +void report_t::entry_report(const entry_t& entry, const string& format) +{ +} + +void report_t::clean_transactions() +{ + session_transactions_iterator walker(session); + pass_down_transactions + (xact_handler_ptr(new clear_transaction_xdata), walker); +} + +void report_t::clean_transactions(entry_t& entry) +{ + entry_transactions_iterator walker(entry); + pass_down_transactions(xact_handler_ptr(new clear_transaction_xdata), walker); +} + +void report_t::clean_accounts() +{ + accounts_iterator acct_walker(*session.master); + pass_down_accounts<accounts_iterator> + (acct_handler_ptr(new clear_account_xdata), acct_walker); } value_t report_t::abbrev(expr::call_scope_t& args) |