diff options
-rw-r--r-- | config.cc | 59 | ||||
-rw-r--r-- | config.h | 3 | ||||
-rw-r--r-- | valexpr.cc | 14 | ||||
-rw-r--r-- | valexpr.h | 6 | ||||
-rw-r--r-- | walk.cc | 38 | ||||
-rw-r--r-- | walk.h | 99 |
6 files changed, 190 insertions, 29 deletions
@@ -76,6 +76,8 @@ void config_t::reset() secondary_predicate = ""; display_predicate = ""; + descend_expr = ""; + head_entries = 0; tail_entries = 0; @@ -322,6 +324,8 @@ config_t::chain_xact_handlers(const std::string& command, account_t * master, std::list<item_handler<transaction_t> *>& ptrs) { + bool remember_components = false; + item_handler<transaction_t> * formatter = NULL; ptrs.push_back(formatter = base_formatter); @@ -348,6 +352,30 @@ config_t::chain_xact_handlers(const std::string& command, // transactions are included or excluded from the running total. ptrs.push_back(formatter = new calc_transactions(formatter)); + // 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)); + 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++) + ptrs.push_back(formatter = + new component_transactions(formatter, *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). @@ -400,18 +428,22 @@ config_t::chain_xact_handlers(const std::string& command, // reports all the transactions that fall on each subsequent day // of the week. if (show_subtotal && ! (command == "b" || command == "E")) - ptrs.push_back(formatter = new subtotal_transactions(formatter)); + ptrs.push_back(formatter = + new subtotal_transactions(formatter, remember_components)); if (days_of_the_week) - ptrs.push_back(formatter = new dow_transactions(formatter)); + ptrs.push_back(formatter = + new dow_transactions(formatter, remember_components)); else if (by_payee) - ptrs.push_back(formatter = new by_payee_transactions(formatter)); + ptrs.push_back(formatter = + new by_payee_transactions(formatter, remember_components)); if (! report_period.empty()) { ptrs.push_back(formatter = new interval_transactions(formatter, report_period, - report_period_sort)); + report_period_sort, + remember_components)); ptrs.push_back(formatter = new sort_transactions(formatter, "d")); } @@ -927,6 +959,23 @@ OPT_BEGIN(related, "r") { config->show_related = true; } OPT_END(related); +OPT_BEGIN(descend, "") { + std::string arg(optarg); + std::string::size_type beg = 0; + config->descend_expr = ""; + for (std::string::size_type pos = arg.find(';'); + pos != std::string::npos; + beg = pos + 1, pos = arg.find(';', beg)) + config->descend_expr += (std::string("t=={") + + std::string(arg, beg, pos) + "};"); + config->descend_expr += (std::string("t=={") + + std::string(arg, beg) + "}"); +} OPT_END(descend); + +OPT_BEGIN(descend_if, "") { + config->descend_expr = optarg; +} OPT_END(descend_if); + OPT_BEGIN(period, "p:") { if (config->report_period.empty()) { config->report_period = optarg; @@ -1157,6 +1206,8 @@ option_t config_options[CONFIG_OPTIONS_SIZE] = { { "current", 'c', false, opt_current, false }, { "date-format", 'y', true, opt_date_format, false }, { "debug", '\0', true, opt_debug, false }, + { "descend", '\0', true, opt_descend, false }, + { "descend-if", '\0', true, opt_descend_if, false }, { "deviation", 'D', false, opt_deviation, false }, { "display", 'd', true, opt_display, false }, { "dow", '\0', false, opt_dow, false }, @@ -44,6 +44,7 @@ class config_t std::string sort_string; std::string amount_expr; std::string total_expr; + std::string descend_expr; std::string forecast_limit; std::string reconcile_balance; std::string reconcile_date; @@ -106,7 +107,7 @@ class config_t std::list<item_handler<transaction_t> *>& ptrs); }; -#define CONFIG_OPTIONS_SIZE 84 +#define CONFIG_OPTIONS_SIZE 86 extern option_t config_options[CONFIG_OPTIONS_SIZE]; void option_help(std::ostream& out); @@ -1578,6 +1578,17 @@ bool write_value_expr(std::ostream& out, case value_expr_t::COST_TOTAL: out << "total_cost"; break; case value_expr_t::F_NOW: out << "now"; break; + case value_expr_t::VALUE_EXPR: + if (write_value_expr(out, amount_expr->parsed, + node_to_find, start_pos, end_pos)) + found = true; + break; + case value_expr_t::TOTAL_EXPR: + if (write_value_expr(out, total_expr->parsed, + node_to_find, start_pos, end_pos)) + found = true; + break; + case value_expr_t::F_ARITH_MEAN: out << "average("; if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos)) @@ -1883,6 +1894,9 @@ void dump_value_expr(std::ostream& out, const value_expr_t * node, case value_expr_t::PRICE_TOTAL: out << "PRICE_TOTAL"; break; case value_expr_t::COST_TOTAL: out << "COST_TOTAL"; break; + case value_expr_t::VALUE_EXPR: out << "VALUE_EXPR"; break; + case value_expr_t::TOTAL_EXPR: out << "TOTAL_EXPR"; break; + case value_expr_t::F_NOW: out << "F_NOW"; break; case value_expr_t::F_ARITH_MEAN: out << "F_ARITH_MEAN"; break; case value_expr_t::F_ABS: out << "F_ABS"; break; @@ -427,6 +427,12 @@ public: guarded_compute(parsed, temp, details, context); return temp; } + + friend bool write_value_expr(std::ostream& out, + const value_expr_t * node, + const value_expr_t * node_to_find, + unsigned long * start_pos, + unsigned long * end_pos); }; extern std::auto_ptr<value_expr> amount_expr; @@ -181,7 +181,8 @@ void handle_value(const value_t& value, unsigned int flags, std::list<transaction_t>& temps, item_handler<transaction_t>& handler, - const std::time_t date = 0) + const std::time_t date = 0, + transactions_list * component_xacts = NULL) { temps.push_back(transaction_t(account)); transaction_t& xact(temps.back()); @@ -189,6 +190,12 @@ void handle_value(const value_t& value, xact.flags |= TRANSACTION_BULK_ALLOC; entry->add_transaction(&xact); + // If there are component transactions to associate with this + // temporary, do so now. + + if (component_xacts) + transaction_xdata(xact).copy_component_xacts(*component_xacts); + // If the account for this transaction is all virtual, then report // the transaction as such. This allows subtotal reports to show // "(Account)" for accounts that contain only virtual transactions. @@ -343,6 +350,17 @@ void changed_value_transactions::operator()(transaction_t& xact) last_xact = &xact; } +void component_transactions::operator()(transaction_t& xact) +{ + if (handler && pred(xact)) { + if (transaction_has_xdata(xact) && + transaction_xdata_(xact).have_component_xacts()) + transaction_xdata_(xact).walk_component_xacts(*handler); + else + (*handler)(xact); + } +} + void subtotal_transactions::report_subtotal(const char * spec_fmt) { char buf[256]; @@ -364,7 +382,7 @@ void subtotal_transactions::report_subtotal(const char * spec_fmt) i != values.end(); i++) handle_value((*i).second.value, (*i).second.account, &entry, 0, - xact_temps, *handler, finish); + xact_temps, *handler, finish, &(*i).second.components); values.clear(); } @@ -383,9 +401,18 @@ void subtotal_transactions::operator()(transaction_t& xact) if (i == values.end()) { value_t temp; add_transaction_to(xact, temp); - values.insert(values_pair(acct->fullname(), acct_value_t(acct, temp))); + std::pair<values_map::iterator, bool> result + = values.insert(values_pair(acct->fullname(), + acct_value_t(acct, temp))); + assert(result.second); + + if (remember_components) + (*result.first).second.components.push_back(&xact); } else { add_transaction_to(xact, (*i).second.value); + + if (remember_components) + (*i).second.components.push_back(&xact); } // If the account for this transaction is all virtual, mark it as @@ -478,8 +505,9 @@ void by_payee_transactions::operator()(transaction_t& xact) { payee_subtotals_map::iterator i = payee_subtotals.find(xact.entry->payee); if (i == payee_subtotals.end()) { - payee_subtotals_pair temp(xact.entry->payee, - new subtotal_transactions(handler)); + payee_subtotals_pair + temp(xact.entry->payee, + new subtotal_transactions(handler, remember_components)); std::pair<payee_subtotals_map::iterator, bool> result = payee_subtotals.insert(temp); @@ -94,8 +94,43 @@ struct transaction_xdata_t account_t * account; void * ptr; + transactions_list * component_xacts; + transaction_xdata_t() - : index(0), dflags(0), date(0), account(0), ptr(0) {} + : index(0), dflags(0), date(0), account(NULL), ptr(NULL), + component_xacts(NULL) { + DEBUG_PRINT("ledger.memory.ctors", "ctor transaction_xdata_t " << this); + } + + ~transaction_xdata_t() { + DEBUG_PRINT("ledger.memory.dtors", "dtor transaction_xdata_t " << this); + if (component_xacts) + delete component_xacts; + } + + void remember_xact(transaction_t& xact) { + if (! component_xacts) + component_xacts = new transactions_list; + component_xacts->push_back(&xact); + } + + bool have_component_xacts() const { + return component_xacts != NULL && ! component_xacts->empty(); + } + + void copy_component_xacts(transactions_list& xacts) { + for (transactions_list::const_iterator i = xacts.begin(); + i != xacts.end(); + i++) + remember_xact(**i); + } + + void walk_component_xacts(item_handler<transaction_t>& handler) const { + for (transactions_list::const_iterator i = component_xacts->begin(); + i != component_xacts->end(); + i++) + handler(**i); + } }; inline bool transaction_has_xdata(const transaction_t& xact) { @@ -323,6 +358,22 @@ class collapse_transactions : public item_handler<transaction_t> virtual void operator()(transaction_t& xact); }; +class component_transactions : public item_handler<transaction_t> +{ + item_predicate<transaction_t> pred; + + public: + component_transactions(item_handler<transaction_t> * handler, + const value_expr_t * predicate) + : item_handler<transaction_t>(handler), pred(predicate) {} + + component_transactions(item_handler<transaction_t> * handler, + const std::string& predicate) + : item_handler<transaction_t>(handler), pred(predicate) {} + + virtual void operator()(transaction_t& xact); +}; + class related_transactions : public item_handler<transaction_t> { transactions_list transactions; @@ -379,8 +430,10 @@ class changed_value_transactions : public item_handler<transaction_t> class subtotal_transactions : public item_handler<transaction_t> { struct acct_value_t { - account_t * account; - value_t value; + account_t * account; + value_t value; + + transactions_list components; acct_value_t(account_t * a) : account(a) {} acct_value_t(account_t * a, value_t& v) : account(a), value(v) {} @@ -393,6 +446,7 @@ class subtotal_transactions : public item_handler<transaction_t> protected: values_map values; + bool remember_components; std::list<entry_t> entry_temps; std::list<transaction_t> xact_temps; @@ -401,15 +455,16 @@ class subtotal_transactions : public item_handler<transaction_t> std::time_t start; std::time_t finish; - subtotal_transactions(item_handler<transaction_t> * handler) - : item_handler<transaction_t>(handler), start(0), finish(0) {} + subtotal_transactions(item_handler<transaction_t> * handler, + bool _remember_components = false) + : item_handler<transaction_t>(handler), + remember_components(_remember_components), start(0), finish(0) {} #ifdef DEBUG_ENABLED subtotal_transactions(const subtotal_transactions&) { assert(0); } #endif - - ~subtotal_transactions() { + virtual ~subtotal_transactions() { clear_entries_transactions(entry_temps); } @@ -442,9 +497,11 @@ class interval_transactions : public subtotal_transactions public: interval_transactions(item_handler<transaction_t> * _handler, const interval_t& _interval, - const value_expr_t * sort_order = NULL) - : subtotal_transactions(_handler), interval(_interval), - last_xact(NULL), started(false), sorter(NULL) { + const value_expr_t * sort_order = NULL, + bool remember_components = false) + : subtotal_transactions(_handler, remember_components), + interval(_interval), last_xact(NULL), started(false), + sorter(NULL) { if (sort_order) { sorter = new sort_transactions(handler, sort_order); handler = sorter; @@ -452,15 +509,16 @@ class interval_transactions : public subtotal_transactions } interval_transactions(item_handler<transaction_t> * _handler, const std::string& _interval, - const std::string& sort_order = "") - : subtotal_transactions(_handler), interval(_interval), - last_xact(NULL), started(false), sorter(NULL) { + const std::string& sort_order = "", + bool remember_components = false) + : subtotal_transactions(_handler, remember_components), + interval(_interval), last_xact(NULL), started(false), + sorter(NULL) { if (! sort_order.empty()) { sorter = new sort_transactions(handler, sort_order); handler = sorter; } } - virtual ~interval_transactions() { if (sorter) delete sorter; @@ -482,11 +540,13 @@ class by_payee_transactions : public item_handler<transaction_t> typedef std::pair<std::string, subtotal_transactions *> payee_subtotals_pair; payee_subtotals_map payee_subtotals; + bool remember_components; public: - by_payee_transactions(item_handler<transaction_t> * handler) - : item_handler<transaction_t>(handler) {} - + by_payee_transactions(item_handler<transaction_t> * handler, + bool _remember_components = false) + : item_handler<transaction_t>(handler), + remember_components(_remember_components) {} virtual ~by_payee_transactions(); virtual void flush(); @@ -514,8 +574,9 @@ class dow_transactions : public subtotal_transactions transactions_list days_of_the_week[7]; public: - dow_transactions(item_handler<transaction_t> * handler) - : subtotal_transactions(handler) {} + dow_transactions(item_handler<transaction_t> * handler, + bool remember_components = false) + : subtotal_transactions(handler, remember_components) {} virtual void flush(); virtual void operator()(transaction_t& xact) { |