improvements to transaction formatting

This commit is contained in:
John Wiegley 2004-08-06 21:38:27 -04:00
parent 88df796880
commit 5db1e1165b
7 changed files with 232 additions and 127 deletions

View file

@ -220,7 +220,7 @@ amount_t& amount_t::operator=(const double value)
amount_t& amount_t::operator+=(const amount_t& amt) amount_t& amount_t::operator+=(const amount_t& amt)
{ {
if (amt.quantity) { if (amt.quantity) {
assert(commodity == amt.commodity); assert(! commodity || commodity == amt.commodity);
INIT(); INIT();
mpz_add(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity)); mpz_add(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity));
} }

122
format.cc
View file

@ -293,30 +293,130 @@ void format_t::format_elements(std::ostream& out,
} }
} }
void format_transaction::operator()(transaction_t * xact) void format_transaction::report_cumulative_subtotal() const
{ {
if (inverted) { if (count == 1) {
xact->total.quantity += - xact->amount; if (! intercept || ! intercept(last_xact))
xact->total.cost += - xact->cost; first_line_format.format_elements(output_stream, details_t(last_xact));
} else { return;
xact->total += *xact;
} }
xact->index = index++;
assert(count > 1);
account_t splits(NULL, "<Total>");
transaction_t splits_total(NULL, &splits);
splits_total.total = subtotal;
balance_t value;
format_t::compute_total(value, details_t(&splits_total));
splits_total.entry = last_entry;
splits_total.total = last_xact->total;
bool first = true;
for (amounts_map::const_iterator i = value.amounts.begin();
i != value.amounts.end();
i++) {
splits_total.amount = (*i).second;
splits_total.cost = (*i).second;
splits_total.total += (*i).second;
if (first) {
if (! intercept || ! intercept(&splits_total))
first_line_format.format_elements(output_stream,
details_t(&splits_total));
first = false;
} else {
next_lines_format.format_elements(output_stream,
details_t(&splits_total));
}
}
}
void format_transaction::operator()(transaction_t * xact) const
{
if (last_xact)
xact->total = last_xact->total;
if (inverted) {
xact->amount.negate();
xact->cost.negate();
}
xact->total += *xact;
xact->index = last_xact ? last_xact->index + 1 : 0;
if (! disp_pred_functor(xact))
return;
xact->flags |= TRANSACTION_DISPLAYED;
// This makes the assumption that transactions from a single entry // This makes the assumption that transactions from a single entry
// will always be grouped together. // are always grouped together.
if (last_entry != xact->entry) if (collapsed) {
// If we've reached a new entry, report on the subtotal
// accumulated thus far.
if (last_entry && last_entry != xact->entry) {
report_cumulative_subtotal();
subtotal = 0;
count = 0;
}
subtotal += *xact;
count++;
} else {
if (last_entry != xact->entry) {
if (! intercept || ! intercept(xact))
first_line_format.format_elements(output_stream, details_t(xact)); first_line_format.format_elements(output_stream, details_t(xact));
else } else {
next_lines_format.format_elements(output_stream, details_t(xact)); next_lines_format.format_elements(output_stream, details_t(xact));
}
}
if (inverted) {
xact->amount.negate();
xact->cost.negate();
}
last_entry = xact->entry; last_entry = xact->entry;
last_xact = xact;
} }
#if 0
bool report_changed_values(transaction_t * xact)
{
static transaction_t * last_xact = NULL;
if (last_xact) {
balance_t prev_bal, cur_bal;
format_t::compute_total(prev_bal, details_t(last_xact));
format_t::compute_total(cur_bal, details_t(xact));
if (balance_t diff = cur_bal - prev_bal) {
entry_t modified_entry;
transaction_t new_xact(&modified_entry, NULL);
modified_entry.date = xact ? xact->entry->date : std::time(NULL);
modified_entry.payee = "Commodities revalued";
new_xact.amount = diff.amount();
format_t::compute_value(diff, details_t(xact));
new_xact.total = cur_bal - diff;
functor(&new_xact);
}
}
last_xact = xact;
}
#endif
void format_account::operator()(const account_t * account, void format_account::operator()(const account_t * account,
const unsigned int max_depth, const unsigned int max_depth,
const bool report_top) const bool report_top) const
{ {
// Don't output the account if only one child will be displayed // Don't output the account if only one child will be displayed
// which shows the exact same amount. jww (2004-08-03): How do // which shows the exact same amount. jww (2004-08-03): How do

View file

@ -87,36 +87,72 @@ class format_transaction
std::ostream& output_stream; std::ostream& output_stream;
const format_t& first_line_format; const format_t& first_line_format;
const format_t& next_lines_format; const format_t& next_lines_format;
const bool collapsed;
const bool inverted; const bool inverted;
unsigned int index;
entry_t * last_entry; item_predicate<transaction_t> disp_pred_functor;
typedef bool (*intercept_t)(transaction_t * xact);
intercept_t intercept;
mutable balance_pair_t subtotal;
mutable unsigned int count;
mutable entry_t * last_entry;
mutable transaction_t * last_xact;
public: public:
format_transaction(std::ostream& _output_stream, format_transaction(std::ostream& _output_stream,
const format_t& _first_line_format, const format_t& _first_line_format,
const format_t& _next_lines_format, const format_t& _next_lines_format,
const bool _inverted) const node_t * display_predicate,
const bool _collapsed = false,
const bool _inverted = false,
intercept_t _intercept = NULL)
: output_stream(_output_stream), : output_stream(_output_stream),
first_line_format(_first_line_format), first_line_format(_first_line_format),
next_lines_format(_next_lines_format), next_lines_format(_next_lines_format),
inverted(_inverted), index(0), last_entry(NULL) {} collapsed(_collapsed), inverted(_inverted),
disp_pred_functor(display_predicate),
intercept(_intercept), count(0),
last_entry(NULL), last_xact(NULL) {}
void operator()(transaction_t * xact); void start() const {}
void finish() const {
if (subtotal)
report_cumulative_subtotal();
}
void report_cumulative_subtotal() const;
void operator()(transaction_t * xact) const;
}; };
// An intercept that can be used to report changes in commodity value
bool report_changed_values(transaction_t * xact);
class format_account class format_account
{ {
std::ostream& output_stream; std::ostream& output_stream;
const format_t& format; const format_t& format;
const account_t * last_account;
item_predicate<account_t> disp_pred_functor;
mutable const account_t * last_account;
public: public:
format_account(std::ostream& _output_stream, const format_t& _format) format_account(std::ostream& _output_stream,
: output_stream(_output_stream), format(_format) {} const format_t& _format,
const node_t * display_predicate = NULL)
: output_stream(_output_stream), format(_format),
disp_pred_functor(display_predicate), last_account(NULL) {}
void start() const {}
void finish() const {}
void operator()(const account_t * account, void operator()(const account_t * account,
const unsigned int max_depth = 1, const unsigned int max_depth = 1,
const bool report_top = false); const bool report_top = false) const;
}; };
} // namespace ledger } // namespace ledger

View file

@ -28,6 +28,8 @@ namespace ledger {
#define TRANSACTION_HANDLED 0x08 #define TRANSACTION_HANDLED 0x08
#define TRANSACTION_DISPLAYED 0x10 #define TRANSACTION_DISPLAYED 0x10
#define TRANSACTION_TRANSIENT (TRANSACTION_HANDLED | TRANSACTION_DISPLAYED)
class entry_t; class entry_t;
class account_t; class account_t;
@ -44,7 +46,8 @@ class transaction_t
unsigned int index; unsigned int index;
transaction_t(entry_t * _entry, account_t * _account) transaction_t(entry_t * _entry, account_t * _account)
: entry(_entry), account(_account), flags(TRANSACTION_NORMAL) {} : entry(_entry), account(_account), flags(TRANSACTION_NORMAL),
index(0) {}
transaction_t(entry_t * _entry, transaction_t(entry_t * _entry,
account_t * _account, account_t * _account,
@ -53,7 +56,7 @@ class transaction_t
unsigned int _flags = TRANSACTION_NORMAL, unsigned int _flags = TRANSACTION_NORMAL,
const std::string& _note = "") const std::string& _note = "")
: entry(_entry), account(_account), amount(_amount), : entry(_entry), account(_account), amount(_amount),
cost(_cost), flags(_flags), note(_note) {} cost(_cost), flags(_flags), note(_note), index(0) {}
}; };

56
main.cc
View file

@ -31,9 +31,6 @@ static const std::string reg_fmt
static const std::string print_fmt static const std::string print_fmt
= "\n%10d %X%C%p\n %-34N %12o\n%/ %-34N %12o\n"; = "\n%10d %X%C%p\n %-34N %12o\n%/ %-34N %12o\n";
static bool show_commodities_revalued = false;
static bool show_commodities_revalued_only = false;
#if 0 #if 0
static void report_value_change(std::ostream& out, static void report_value_change(std::ostream& out,
@ -44,33 +41,6 @@ static void report_value_change(std::ostream& out,
const format_t& first_line_format, const format_t& first_line_format,
const format_t& next_lines_format) const format_t& next_lines_format)
{ {
static std::time_t prev_date = -1;
if (prev_date == -1) {
prev_date = date;
return;
}
item_t temp;
temp.date = prev_date;
temp.total = prev_balance;
balance_t prev_bal = format_t::compute_total(&temp);
temp.date = date;
temp.total = balance;
balance_t cur_bal = format_t::compute_total(&temp);
if (balance_t diff = cur_bal - prev_bal) {
temp.value = diff;
temp.total = balance;
temp.payee = "Commodities revalued";
if (value_predicate(predicate)(&temp)) {
first_line_format.format_elements(out, &temp);
next_lines_format.format_elements(out, &temp);
}
}
prev_date = date;
} }
void register_report(std::ostream& out, void register_report(std::ostream& out,
@ -307,6 +277,9 @@ int main(int argc, char * argv[])
bool show_inverted = false; bool show_inverted = false;
bool show_empty = false; bool show_empty = false;
bool show_commodities_revalued = false;
bool show_commodities_revalued_only = false;
#ifdef DEBUG #ifdef DEBUG
bool debug = false; bool debug = false;
#endif #endif
@ -763,16 +736,19 @@ int main(int argc, char * argv[])
if (command == "b") { if (command == "b") {
format_t format(f); format_t format(f);
walk_accounts(journal->master, format_account(std::cout, format), format_account formatter(std::cout, format, display_predicate.get());
predicate.get(), xact_display_flags, show_subtotals, formatter.start();
show_expanded ? 0 : 1, display_predicate.get(), walk_accounts(journal->master, formatter, predicate.get(),
xact_display_flags, show_subtotals, show_expanded ? 0 : 1,
sort_order.get()); sort_order.get());
formatter.finish();
if (! display_predicate.get() || if (! display_predicate.get() ||
item_predicate<account_t>(display_predicate.get())(journal->master)) { item_predicate<account_t>(display_predicate.get())(journal->master)) {
std::string end_format = "--------------------\n"; std::string end_format = "--------------------\n";
format.reset(end_format + f); format.reset(end_format + f);
format_account(std::cout, format)(journal->master, true); format_account(std::cout, format)(journal->master, true,
display_predicate.get());
} }
} else { } else {
std::string first_line_format; std::string first_line_format;
@ -787,22 +763,26 @@ int main(int argc, char * argv[])
format_t format(first_line_format); format_t format(first_line_format);
format_t nformat(next_lines_format); format_t nformat(next_lines_format);
format_transaction formatter(std::cout, format, nformat, show_inverted);
format_transaction formatter(std::cout, format, nformat,
display_predicate.get(),
! show_subtotals, show_inverted);
formatter.start();
if (! sort_order.get()) { if (! sort_order.get()) {
walk_entries(journal->entries.begin(), journal->entries.end(), walk_entries(journal->entries.begin(), journal->entries.end(),
formatter, predicate.get(), xact_display_flags, formatter, predicate.get(), xact_display_flags);
display_predicate.get());
} else { } else {
transactions_deque transactions_pool; transactions_deque transactions_pool;
walk_entries(journal->entries.begin(), journal->entries.end(), walk_entries(journal->entries.begin(), journal->entries.end(),
collect_transactions(transactions_pool), predicate.get(), collect_transactions(transactions_pool), predicate.get(),
xact_display_flags, display_predicate.get()); xact_display_flags);
std::stable_sort(transactions_pool.begin(), transactions_pool.end(), std::stable_sort(transactions_pool.begin(), transactions_pool.end(),
compare_items<transaction_t>(sort_order.get())); compare_items<transaction_t>(sort_order.get()));
walk_transactions(transactions_pool.begin(), transactions_pool.end(), walk_transactions(transactions_pool.begin(), transactions_pool.end(),
formatter); formatter);
} }
formatter.finish();
} }
// Save the cache, if need be // Save the cache, if need be

View file

@ -136,6 +136,25 @@ inline node_t * find_node(node_t * node, node_t::kind_t type) {
return result; return result;
} }
template <typename T>
class item_predicate
{
const node_t * predicate;
public:
item_predicate(const node_t * _predicate) : predicate(_predicate) {}
bool operator()(const T * item) const {
if (predicate) {
balance_t result;
predicate->compute(result, details_t(item));
return result;
} else {
return true;
}
}
};
} // namespace report } // namespace report
#endif // _REPORT_H #endif // _REPORT_H

77
walk.h
View file

@ -10,25 +10,6 @@
namespace ledger { namespace ledger {
template <typename T>
class item_predicate
{
const node_t * predicate;
public:
item_predicate(const node_t * _predicate) : predicate(_predicate) {}
bool operator()(const T * item) const {
if (predicate) {
balance_t result;
predicate->compute(result, details_t(item));
return result;
} else {
return true;
}
}
};
template <typename T> template <typename T>
struct compare_items { struct compare_items {
const node_t * sort_order; const node_t * sort_order;
@ -59,7 +40,7 @@ class collect_transactions
collect_transactions(transactions_deque& _transactions) collect_transactions(transactions_deque& _transactions)
: transactions(_transactions) {} : transactions(_transactions) {}
void operator()(transaction_t * xact) { void operator()(transaction_t * xact) const {
transactions.push_back(xact); transactions.push_back(xact);
} }
}; };
@ -81,18 +62,15 @@ class ignore_transaction
#define OTHER_TRANSACTIONS 0x02 #define OTHER_TRANSACTIONS 0x02
template <typename Function> template <typename Function>
void handle_transaction(transaction_t * xact, Function functor, void handle_transaction(transaction_t * xact,
item_predicate<transaction_t>& pred_functor, const Function& functor,
unsigned int flags) unsigned int flags)
{ {
if ((flags & MATCHING_TRANSACTIONS) && if ((flags & MATCHING_TRANSACTIONS) &&
! (xact->flags & TRANSACTION_HANDLED)) { ! (xact->flags & TRANSACTION_HANDLED)) {
xact->flags |= TRANSACTION_HANDLED; xact->flags |= TRANSACTION_HANDLED;
if (pred_functor(xact)) {
xact->flags |= TRANSACTION_DISPLAYED;
functor(xact); functor(xact);
} }
}
if (flags & OTHER_TRANSACTIONS) if (flags & OTHER_TRANSACTIONS)
for (transactions_list::iterator i = xact->entry->transactions.begin(); for (transactions_list::iterator i = xact->entry->transactions.begin();
@ -103,35 +81,30 @@ void handle_transaction(transaction_t * xact, Function functor,
continue; continue;
(*i)->flags |= TRANSACTION_HANDLED; (*i)->flags |= TRANSACTION_HANDLED;
if (pred_functor(xact)) {
xact->flags |= TRANSACTION_DISPLAYED;
functor(*i); functor(*i);
} }
}
} }
template <typename Function> template <typename Function>
void walk_entries(entries_list::iterator begin, void walk_entries(entries_list::iterator begin,
entries_list::iterator end, entries_list::iterator end,
Function functor, const Function& functor,
const node_t * predicate, const node_t * predicate,
unsigned int flags, unsigned int flags)
const node_t * display_predicate = NULL)
{ {
item_predicate<transaction_t> pred_functor(predicate); item_predicate<transaction_t> pred_functor(predicate);
item_predicate<transaction_t> disp_pred_functor(display_predicate);
for (entries_list::iterator i = begin; i != end; i++) for (entries_list::iterator i = begin; i != end; i++)
for (transactions_list::iterator j = (*i)->transactions.begin(); for (transactions_list::iterator j = (*i)->transactions.begin();
j != (*i)->transactions.end(); j != (*i)->transactions.end();
j++) j++)
if (pred_functor(*j)) if (pred_functor(*j))
handle_transaction(*j, functor, disp_pred_functor, flags); handle_transaction(*j, functor, flags);
} }
template <typename Function> template <typename Function>
void walk_entries(entries_list::iterator begin, void walk_entries(entries_list::iterator begin,
entries_list::iterator end, Function functor) entries_list::iterator end, const Function& functor)
{ {
for (entries_list::iterator i = begin; i != end; i++) for (entries_list::iterator i = begin; i != end; i++)
for (transactions_list::iterator j = (*i)->transactions.begin(); for (transactions_list::iterator j = (*i)->transactions.begin();
@ -144,7 +117,7 @@ class clear_flags
{ {
public: public:
void operator()(transaction_t * xact) const { void operator()(transaction_t * xact) const {
xact->flags &= ~(TRANSACTION_HANDLED | TRANSACTION_DISPLAYED); xact->flags &= ~TRANSACTION_TRANSIENT;
} }
}; };
@ -156,7 +129,8 @@ inline void clear_transaction_display_flags(entries_list::iterator begin,
template <typename Function> template <typename Function>
void walk_transactions(transactions_list::iterator begin, void walk_transactions(transactions_list::iterator begin,
transactions_list::iterator end, Function functor) transactions_list::iterator end,
const Function& functor)
{ {
for (transactions_list::iterator i = begin; i != end; i++) for (transactions_list::iterator i = begin; i != end; i++)
functor(*i); functor(*i);
@ -164,7 +138,8 @@ void walk_transactions(transactions_list::iterator begin,
template <typename Function> template <typename Function>
void walk_transactions(transactions_deque::iterator begin, void walk_transactions(transactions_deque::iterator begin,
transactions_deque::iterator end, Function functor) transactions_deque::iterator end,
const Function& functor)
{ {
for (transactions_deque::iterator i = begin; i != end; i++) for (transactions_deque::iterator i = begin; i != end; i++)
functor(*i); functor(*i);
@ -187,27 +162,23 @@ inline void sort_accounts(account_t * account,
template <typename Function> template <typename Function>
void walk__accounts(const account_t * account, void walk__accounts(const account_t * account,
Function functor, const Function& functor,
const unsigned int max_depth, const unsigned int max_depth)
item_predicate<account_t>& disp_pred_functor)
{ {
if (disp_pred_functor(account))
functor(account, max_depth); functor(account, max_depth);
for (accounts_map::const_iterator i = account->accounts.begin(); for (accounts_map::const_iterator i = account->accounts.begin();
i != account->accounts.end(); i != account->accounts.end();
i++) i++)
walk__accounts((*i).second, functor, max_depth, disp_pred_functor); walk__accounts((*i).second, functor, max_depth);
} }
template <typename Function> template <typename Function>
void walk__accounts_sorted(const account_t * account, void walk__accounts_sorted(const account_t * account,
Function functor, const Function& functor,
const unsigned int max_depth, const unsigned int max_depth,
const node_t * sort_order, const node_t * sort_order)
item_predicate<account_t>& disp_pred_functor)
{ {
if (disp_pred_functor(account))
functor(account, max_depth); functor(account, max_depth);
accounts_deque accounts; accounts_deque accounts;
@ -223,12 +194,11 @@ void walk__accounts_sorted(const account_t * account,
for (accounts_deque::const_iterator i = accounts.begin(); for (accounts_deque::const_iterator i = accounts.begin();
i != accounts.end(); i != accounts.end();
i++) i++)
walk__accounts_sorted(*i, functor, max_depth, sort_order, walk__accounts_sorted(*i, functor, max_depth, sort_order);
disp_pred_functor);
} }
template <typename Function> template <typename Function>
void for_each_account(account_t * account, Function functor) void for_each_account(account_t * account, const Function& functor)
{ {
functor(account); functor(account);
@ -255,26 +225,23 @@ inline void sum__accounts(account_t * account)
template <typename Function> template <typename Function>
void walk_accounts(account_t * account, void walk_accounts(account_t * account,
Function functor, const Function& functor,
const node_t * predicate, const node_t * predicate,
unsigned int flags, unsigned int flags,
const bool calc_subtotals, const bool calc_subtotals,
const unsigned int max_depth, const unsigned int max_depth,
const node_t * display_predicate = NULL,
const node_t * sort_order = NULL) const node_t * sort_order = NULL)
{ {
item_predicate<transaction_t> pred_functor(predicate); item_predicate<transaction_t> pred_functor(predicate);
item_predicate<account_t> disp_pred_functor(display_predicate);
calc__accounts(account, pred_functor, flags); calc__accounts(account, pred_functor, flags);
if (calc_subtotals) if (calc_subtotals)
sum__accounts(account); sum__accounts(account);
if (sort_order) if (sort_order)
walk__accounts_sorted<Function>(account, functor, max_depth, sort_order, walk__accounts_sorted<Function>(account, functor, max_depth, sort_order);
disp_pred_functor);
else else
walk__accounts<Function>(account, functor, max_depth, disp_pred_functor); walk__accounts<Function>(account, functor, max_depth);
} }
} // namespace ledger } // namespace ledger