summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--config.cc59
-rw-r--r--config.h3
-rw-r--r--valexpr.cc14
-rw-r--r--valexpr.h6
-rw-r--r--walk.cc38
-rw-r--r--walk.h99
6 files changed, 190 insertions, 29 deletions
diff --git a/config.cc b/config.cc
index dd5a1a62..907ab7c7 100644
--- a/config.cc
+++ b/config.cc
@@ -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 },
diff --git a/config.h b/config.h
index f347288c..dce8cca0 100644
--- a/config.h
+++ b/config.h
@@ -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);
diff --git a/valexpr.cc b/valexpr.cc
index 76213506..22bd88d3 100644
--- a/valexpr.cc
+++ b/valexpr.cc
@@ -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;
diff --git a/valexpr.h b/valexpr.h
index 3d260d43..2ce57278 100644
--- a/valexpr.h
+++ b/valexpr.h
@@ -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;
diff --git a/walk.cc b/walk.cc
index f608cb6e..cfb9af28 100644
--- a/walk.cc
+++ b/walk.cc
@@ -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);
diff --git a/walk.h b/walk.h
index a7dc3c08..1e377aba 100644
--- a/walk.h
+++ b/walk.h
@@ -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) {