Added new --descend option.
This commit is contained in:
parent
49ae3b65d5
commit
4f83a2bf8f
6 changed files with 190 additions and 29 deletions
59
config.cc
59
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 },
|
||||
|
|
|
|||
3
config.h
3
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);
|
||||
|
|
|
|||
14
valexpr.cc
14
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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
38
walk.cc
38
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);
|
||||
|
||||
|
|
|
|||
99
walk.h
99
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) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue