summaryrefslogtreecommitdiff
path: root/walk.cc
diff options
context:
space:
mode:
authorJohn Wiegley <johnw@newartisans.com>2004-11-08 06:43:11 +0000
committerJohn Wiegley <johnw@newartisans.com>2008-04-13 02:40:47 -0400
commitc9fb11bd60a2170fb896d77ff8d7706f563ad597 (patch)
tree42bdf09e7d8727ba31d1d8dae9b4eb4b2a605441 /walk.cc
parentfa2ceaed13c031add578ee8eb33da0c9980b9fb1 (diff)
downloadfork-ledger-c9fb11bd60a2170fb896d77ff8d7706f563ad597.tar.gz
fork-ledger-c9fb11bd60a2170fb896d77ff8d7706f563ad597.tar.bz2
fork-ledger-c9fb11bd60a2170fb896d77ff8d7706f563ad597.zip
updated to version 2.0
Diffstat (limited to 'walk.cc')
-rw-r--r--walk.cc1099
1 files changed, 1099 insertions, 0 deletions
diff --git a/walk.cc b/walk.cc
new file mode 100644
index 00000000..d94723c2
--- /dev/null
+++ b/walk.cc
@@ -0,0 +1,1099 @@
+#include "walk.h"
+#include "format.h"
+#include "textual.h"
+#include "util.h"
+
+namespace ledger {
+
+std::list<transaction_xdata_t> transactions_xdata;
+std::list<void **> transactions_xdata_ptrs;
+
+std::list<account_xdata_t> accounts_xdata;
+std::list<void **> accounts_xdata_ptrs;
+
+template <>
+bool compare_items<transaction_t>::operator()(const transaction_t * left,
+ const transaction_t * right)
+{
+ assert(left);
+ assert(right);
+
+ transaction_xdata_t& lxdata(transaction_xdata(*left));
+ if (! (lxdata.dflags & TRANSACTION_SORT_CALC)) {
+ sort_order->compute(lxdata.sort_value, details_t(*left));
+ lxdata.dflags |= TRANSACTION_SORT_CALC;
+ }
+
+ transaction_xdata_t& rxdata(transaction_xdata(*right));
+ if (! (rxdata.dflags & TRANSACTION_SORT_CALC)) {
+ sort_order->compute(rxdata.sort_value, details_t(*right));
+ rxdata.dflags |= TRANSACTION_SORT_CALC;
+ }
+
+ return lxdata.sort_value < rxdata.sort_value;
+}
+
+transaction_xdata_t& transaction_xdata(const transaction_t& xact)
+{
+ if (! xact.data) {
+ transactions_xdata.push_back(transaction_xdata_t());
+ xact.data = &transactions_xdata.back();
+ transactions_xdata_ptrs.push_back(&xact.data);
+ }
+ return *((transaction_xdata_t *) xact.data);
+}
+
+void add_transaction_to(const transaction_t& xact, value_t& value)
+{
+ if (transaction_has_xdata(xact) &&
+ transaction_xdata_(xact).dflags & TRANSACTION_COMPOSITE)
+ value += transaction_xdata_(xact).composite_amount;
+ else if (xact.cost || value)
+ value.add(xact.amount, xact.cost);
+ else
+ value = xact.amount;
+}
+
+void set_account_value::operator()(transaction_t& xact)
+{
+ add_transaction_to(xact, account_xdata(*xact.account).value);
+ account_xdata_(*xact.account).count++;
+ if (xact.flags & TRANSACTION_VIRTUAL)
+ account_xdata_(*xact.account).virtuals++;
+
+ item_handler<transaction_t>::operator()(xact);
+}
+
+void sort_transactions::flush()
+{
+ std::stable_sort(transactions.begin(), transactions.end(),
+ compare_items<transaction_t>(sort_order));
+
+ for (transactions_deque::iterator i = transactions.begin();
+ i != transactions.end();
+ i++) {
+ transaction_xdata(**i).dflags &= ~TRANSACTION_SORT_CALC;
+ item_handler<transaction_t>::operator()(**i);
+ }
+
+ item_handler<transaction_t>::flush();
+
+ transactions.clear();
+}
+
+void calc_transactions::operator()(transaction_t& xact)
+{
+ transaction_xdata_t& xdata(transaction_xdata(xact));
+
+ if (last_xact && transaction_has_xdata(*last_xact)) {
+ xdata.total += transaction_xdata_(*last_xact).total;
+ xdata.index = transaction_xdata_(*last_xact).index + 1;
+ } else {
+ xdata.index = 0;
+ }
+
+ if (! (xdata.dflags & TRANSACTION_NO_TOTAL))
+ add_transaction_to(xact, xdata.total);
+
+ item_handler<transaction_t>::operator()(xact);
+
+ last_xact = &xact;
+}
+
+void invert_transactions::operator()(transaction_t& xact)
+{
+ if (transaction_has_xdata(xact) &&
+ transaction_xdata_(xact).dflags & TRANSACTION_COMPOSITE) {
+ transaction_xdata_(xact).composite_amount.negate();
+ } else {
+ xact.amount.negate();
+ if (xact.cost)
+ xact.cost->negate();
+ }
+
+ item_handler<transaction_t>::operator()(xact);
+}
+
+
+static inline
+void handle_value(const value_t& value,
+ account_t * account,
+ entry_t * entry,
+ unsigned int flags,
+ std::list<transaction_t>& temps,
+ item_handler<transaction_t>& handler,
+ const std::time_t date = 0)
+{
+ temps.push_back(transaction_t(account));
+ transaction_t& xact(temps.back());
+ xact.entry = entry;
+ xact.flags |= TRANSACTION_BULK_ALLOC;
+ entry->add_transaction(&xact);
+
+ // 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.
+
+ if (account && account_has_xdata(*account))
+ if (! (account_xdata_(*account).dflags & ACCOUNT_HAS_NON_VIRTUALS)) {
+ xact.flags |= TRANSACTION_VIRTUAL;
+ if (! (account_xdata_(*account).dflags & ACCOUNT_HAS_UNB_VIRTUALS))
+ xact.flags |= TRANSACTION_BALANCE;
+ }
+
+ transaction_xdata_t& xdata(transaction_xdata(xact));
+
+ switch (value.type) {
+ case value_t::BOOLEAN:
+ xact.amount = *((bool *) value.data);
+ break;
+ case value_t::INTEGER:
+ xact.amount = *((long *) value.data);
+ break;
+ case value_t::AMOUNT:
+ xact.amount = *((amount_t *) value.data);
+ break;
+
+ case value_t::BALANCE:
+ case value_t::BALANCE_PAIR:
+ xdata.composite_amount = value;
+ flags |= TRANSACTION_COMPOSITE;
+ break;
+ }
+
+ if (date)
+ xdata.date = date;
+ if (flags)
+ xdata.dflags |= flags;
+
+ handler(xact);
+}
+
+void collapse_transactions::report_subtotal()
+{
+ assert(count >= 1);
+
+ if (count == 1) {
+ item_handler<transaction_t>::operator()(*last_xact);
+ } else {
+ entry_temps.push_back(entry_t());
+ entry_t& entry = entry_temps.back();
+ entry.payee = last_entry->payee;
+ entry.date = last_entry->date;
+
+ handle_value(subtotal, &totals_account, last_entry, 0, xact_temps,
+ *handler);
+ }
+
+ last_entry = NULL;
+ last_xact = NULL;
+ subtotal = 0L;
+ count = 0;
+}
+
+void collapse_transactions::operator()(transaction_t& xact)
+{
+ // If we've reached a new entry, report on the subtotal
+ // accumulated thus far.
+
+ if (last_entry && last_entry != xact.entry && count > 0)
+ report_subtotal();
+
+ add_transaction_to(xact, subtotal);
+ count++;
+
+ last_entry = xact.entry;
+ last_xact = &xact;
+}
+
+void related_transactions::flush()
+{
+ if (transactions.size() > 0)
+ for (transactions_list::iterator i = transactions.begin();
+ i != transactions.end();
+ i++)
+ for (transactions_list::iterator j = (*i)->entry->transactions.begin();
+ j != (*i)->entry->transactions.end();
+ j++) {
+ transaction_xdata_t& xdata = transaction_xdata(**j);
+ if (! (xdata.dflags & TRANSACTION_HANDLED) &&
+ (! (xdata.dflags & TRANSACTION_RECEIVED) ?
+ ! ((*j)->flags & (TRANSACTION_AUTO | TRANSACTION_VIRTUAL)) :
+ also_matching)) {
+ xdata.dflags |= TRANSACTION_HANDLED;
+ item_handler<transaction_t>::operator()(**j);
+ }
+ }
+
+ item_handler<transaction_t>::flush();
+}
+
+void changed_value_transactions::output_diff(const std::time_t current)
+{
+ value_t cur_bal;
+
+ transaction_xdata(*last_xact).date = current;
+ compute_total(cur_bal, details_t(*last_xact));
+ cur_bal.round();
+ transaction_xdata(*last_xact).date = 0;
+
+ if (value_t diff = cur_bal - last_balance) {
+ entry_temps.push_back(entry_t());
+ entry_t& entry = entry_temps.back();
+ entry.payee = "Commodities revalued";
+ entry.date = current;
+
+ handle_value(diff, NULL, &entry, TRANSACTION_NO_TOTAL, xact_temps,
+ *handler);
+ }
+}
+
+void changed_value_transactions::operator()(transaction_t& xact)
+{
+ if (last_xact) {
+ std::time_t moment = 0;
+ if (transaction_has_xdata(*last_xact))
+ moment = transaction_xdata_(*last_xact).date;
+ if (! moment)
+ moment = xact.entry->date;
+ output_diff(moment);
+ }
+
+ if (changed_values_only)
+ transaction_xdata(xact).dflags |= TRANSACTION_DISPLAYED;
+
+ item_handler<transaction_t>::operator()(xact);
+
+ compute_total(last_balance, details_t(xact));
+ last_balance.round();
+
+ last_xact = &xact;
+}
+
+void subtotal_transactions::report_subtotal(const char * spec_fmt)
+{
+ char buf[256];
+
+ if (! spec_fmt) {
+ std::string fmt = "- ";
+ fmt += format_t::date_format;
+ std::strftime(buf, 255, fmt.c_str(), std::localtime(&finish));
+ } else {
+ std::strftime(buf, 255, spec_fmt, std::localtime(&finish));
+ }
+
+ entry_temps.push_back(entry_t());
+ entry_t& entry = entry_temps.back();
+ entry.payee = buf;
+ entry.date = start;
+
+ for (values_map::iterator i = values.begin();
+ i != values.end();
+ i++)
+ handle_value((*i).second, (*i).first, &entry, 0, xact_temps,
+ *handler, finish);
+
+ values.clear();
+}
+
+void subtotal_transactions::operator()(transaction_t& xact)
+{
+ if (! start || std::difftime(xact.entry->date, start) < 0)
+ start = xact.entry->date;
+ if (! finish || std::difftime(xact.entry->date, finish) > 0)
+ finish = xact.entry->date;
+
+ values_map::iterator i = values.find(xact.account);
+ if (i == values.end()) {
+ value_t temp;
+ add_transaction_to(xact, temp);
+ values.insert(values_pair(xact.account, temp));
+ } else {
+ add_transaction_to(xact, (*i).second);
+ }
+
+ // If the account for this transaction is all virtual, mark it as
+ // such, so that `handle_value' can show "(Account)" for accounts
+ // that contain only virtual transactions.
+
+ if (! (xact.flags & TRANSACTION_VIRTUAL))
+ account_xdata(*xact.account).dflags |= ACCOUNT_HAS_NON_VIRTUALS;
+ else if (! (xact.flags & TRANSACTION_BALANCE))
+ account_xdata(*xact.account).dflags |= ACCOUNT_HAS_UNB_VIRTUALS;
+}
+
+void interval_transactions::report_subtotal(const std::time_t moment)
+{
+ assert(last_xact);
+
+ start = interval.begin;
+ if (moment)
+ finish = moment - 86400;
+ else
+ finish = last_xact->entry->date;
+
+ subtotal_transactions::report_subtotal();
+
+ if (sorter)
+ subtotal_transactions::flush();
+
+ last_xact = NULL;
+}
+
+void interval_transactions::operator()(transaction_t& xact)
+{
+ if ((interval.begin &&
+ std::difftime(xact.entry->date, interval.begin) < 0) ||
+ (interval.end &&
+ std::difftime(xact.entry->date, interval.end) >= 0))
+ return;
+
+ if (interval) {
+ if (! started) {
+ if (! interval.begin)
+ interval.start(xact.entry->date);
+ start = interval.begin;
+ started = true;
+ }
+
+ std::time_t quant = interval.increment(interval.begin);
+ if (std::difftime(xact.entry->date, quant) >= 0) {
+ if (last_xact)
+ report_subtotal(quant);
+
+ std::time_t temp;
+ while (std::difftime(xact.entry->date,
+ temp = interval.increment(quant)) >= 0) {
+ if (quant == temp)
+ break;
+ quant = temp;
+ }
+ start = interval.begin = quant;
+ }
+
+ subtotal_transactions::operator()(xact);
+ } else {
+ item_handler<transaction_t>::operator()(xact);
+ }
+
+ last_xact = &xact;
+}
+
+by_payee_transactions::~by_payee_transactions()
+{
+ for (payee_subtotals_map::iterator i = payee_subtotals.begin();
+ i != payee_subtotals.end();
+ i++)
+ delete (*i).second;
+}
+
+void by_payee_transactions::flush()
+{
+ for (payee_subtotals_map::iterator i = payee_subtotals.begin();
+ i != payee_subtotals.end();
+ i++)
+ (*i).second->report_subtotal((*i).first.c_str());
+
+ item_handler<transaction_t>::flush();
+
+ payee_subtotals.clear();
+}
+
+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));
+ std::pair<payee_subtotals_map::iterator, bool> result
+ = payee_subtotals.insert(temp);
+
+ assert(result.second);
+ if (! result.second)
+ return;
+ i = result.first;
+ }
+
+ if (std::difftime(xact.entry->date, (*i).second->start) > 0)
+ (*i).second->start = xact.entry->date;
+
+ (*(*i).second)(xact);
+}
+
+void set_comm_as_payee::operator()(transaction_t& xact)
+{
+ entry_temps.push_back(*xact.entry);
+ entry_t& entry = entry_temps.back();
+ entry.date = xact.entry->date;
+ entry.state = xact.entry->state;
+ entry.code = xact.entry->code;
+ entry.payee = xact.amount.commodity().symbol;
+
+ xact_temps.push_back(xact);
+ transaction_t& temp = xact_temps.back();
+ temp.entry = &entry;
+ temp.flags |= TRANSACTION_BULK_ALLOC;
+ entry.add_transaction(&temp);
+
+ item_handler<transaction_t>::operator()(temp);
+}
+
+void dow_transactions::flush()
+{
+ for (int i = 0; i < 7; i++) {
+ start = finish = 0;
+ for (transactions_list::iterator d = days_of_the_week[i].begin();
+ d != days_of_the_week[i].end();
+ d++)
+ subtotal_transactions::operator()(**d);
+ subtotal_transactions::report_subtotal("%As");
+ days_of_the_week[i].clear();
+ }
+
+ subtotal_transactions::flush();
+}
+
+void generate_transactions::add_period_entries
+ (period_entries_list& period_entries)
+{
+ for (period_entries_list::iterator i = period_entries.begin();
+ i != period_entries.end();
+ i++)
+ for (transactions_list::iterator j = (*i)->transactions.begin();
+ j != (*i)->transactions.end();
+ j++)
+ add_transaction((*i)->period, **j);
+}
+
+void generate_transactions::add_transaction(const interval_t& period,
+ transaction_t& xact)
+{
+ pending_xacts.push_back(pending_xacts_pair(period, &xact));
+}
+
+void budget_transactions::report_budget_items(const std::time_t moment)
+{
+ if (pending_xacts.size() > 0) {
+ std::list<pending_xacts_list::iterator> to_clear;
+
+ bool reported;
+ do {
+ reported = false;
+ for (pending_xacts_list::iterator i = pending_xacts.begin();
+ i != pending_xacts.end();
+ i++) {
+ std::time_t& begin = (*i).first.begin;
+ if (! begin) {
+ (*i).first.start(moment);
+ begin = (*i).first.begin;
+ }
+
+ if (std::difftime(begin, moment) < 0 &&
+ (! (*i).first.end || std::difftime(begin, (*i).first.end) < 0)) {
+ transaction_t& xact = *(*i).second;
+
+ DEBUG_PRINT("ledger.walk.budget", "Reporting budget for "
+ << xact.account->fullname());
+ DEBUG_PRINT_TIME("ledger.walk.budget", begin);
+ DEBUG_PRINT_TIME("ledger.walk.budget", moment);
+
+ entry_temps.push_back(entry_t());
+ entry_t& entry = entry_temps.back();
+ entry.payee = "Budget entry";
+ entry.date = begin;
+
+ xact_temps.push_back(xact);
+ transaction_t& temp = xact_temps.back();
+ temp.entry = &entry;
+ temp.flags |= TRANSACTION_AUTO;
+ temp.amount.negate();
+ temp.flags |= TRANSACTION_BULK_ALLOC;
+ entry.add_transaction(&temp);
+
+ begin = (*i).first.increment(begin);
+
+ item_handler<transaction_t>::operator()(temp);
+
+ reported = true;
+ }
+ }
+ } while (reported);
+
+ for (std::list<pending_xacts_list::iterator>::iterator
+ i = to_clear.begin();
+ i != to_clear.end();
+ i++)
+ pending_xacts.erase(*i);
+ }
+}
+
+void budget_transactions::operator()(transaction_t& xact)
+{
+ bool xact_in_budget = false;
+
+ for (pending_xacts_list::iterator i = pending_xacts.begin();
+ i != pending_xacts.end();
+ i++)
+ for (account_t * acct = xact.account; acct; acct = acct->parent) {
+ if (acct == (*i).second->account) {
+ xact_in_budget = true;
+ // Report the transaction as if it had occurred in the parent
+ // account.
+ if (xact.account != acct)
+ xact.account = acct;
+ goto handle;
+ }
+ }
+
+ handle:
+ if (xact_in_budget && flags & BUDGET_BUDGETED) {
+ report_budget_items(xact.entry->date);
+ item_handler<transaction_t>::operator()(xact);
+ }
+ else if (! xact_in_budget && flags & BUDGET_UNBUDGETED) {
+ item_handler<transaction_t>::operator()(xact);
+ }
+}
+
+void forecast_transactions::add_transaction(const interval_t& period,
+ transaction_t& xact)
+{
+ generate_transactions::add_transaction(period, xact);
+
+ interval_t& i = pending_xacts.back().first;
+ if (! i.begin) {
+ i.start(now);
+ i.begin = i.increment(i.begin);
+ } else {
+ while (std::difftime(i.begin, now) < 0)
+ i.begin = i.increment(i.begin);
+ }
+}
+
+void forecast_transactions::flush()
+{
+ transactions_list passed;
+ std::time_t last = 0;
+
+ while (pending_xacts.size() > 0) {
+ pending_xacts_list::iterator least = pending_xacts.begin();
+ for (pending_xacts_list::iterator i = ++pending_xacts.begin();
+ i != pending_xacts.end();
+ i++)
+ if (std::difftime((*i).first.begin, (*least).first.begin) < 0)
+ least = i;
+
+ std::time_t& begin = (*least).first.begin;
+
+ if ((*least).first.end &&
+ std::difftime(begin, (*least).first.end) >= 0) {
+ pending_xacts.erase(least);
+ passed.remove((*least).second);
+ continue;
+ }
+
+ transaction_t& xact = *(*least).second;
+
+ entry_temps.push_back(entry_t());
+ entry_t& entry = entry_temps.back();
+ entry.payee = "Forecast entry";
+ entry.date = begin;
+
+ xact_temps.push_back(xact);
+ transaction_t& temp = xact_temps.back();
+ temp.entry = &entry;
+ temp.flags |= TRANSACTION_AUTO;
+ temp.flags |= TRANSACTION_BULK_ALLOC;
+ entry.add_transaction(&temp);
+
+ std::time_t next = (*least).first.increment(begin);
+ if (std::difftime(next, begin) < 0 || // wraparound
+ (last && std::difftime(next, last) > 5 * 365 * 24 * 60 * 60))
+ break;
+ begin = next;
+
+ item_handler<transaction_t>::operator()(temp);
+
+ if (transaction_has_xdata(temp) &&
+ transaction_xdata_(temp).dflags & TRANSACTION_MATCHES) {
+ if (! pred(temp))
+ break;
+ last = temp.entry->date;
+ passed.clear();
+ } else {
+ bool found = false;
+ for (transactions_list::iterator i = passed.begin();
+ i != passed.end();
+ i++)
+ if (*i == &xact) {
+ found = true;
+ break;
+ }
+
+ if (! found) {
+ passed.push_back(&xact);
+ if (passed.size() >= pending_xacts.size())
+ break;
+ }
+ }
+ }
+
+ item_handler<transaction_t>::flush();
+}
+
+void clear_transactions_xdata()
+{
+ transactions_xdata.clear();
+
+ for (std::list<void **>::iterator i = transactions_xdata_ptrs.begin();
+ i != transactions_xdata_ptrs.end();
+ i++)
+ **i = NULL;
+}
+
+template <>
+bool compare_items<account_t>::operator()(const account_t * left,
+ const account_t * right)
+{
+ assert(left);
+ assert(right);
+
+ account_xdata_t& lxdata(account_xdata(*left));
+ if (! (lxdata.dflags & ACCOUNT_SORT_CALC)) {
+ sort_order->compute(lxdata.sort_value, details_t(*left));
+ lxdata.dflags |= ACCOUNT_SORT_CALC;
+ }
+
+ account_xdata_t& rxdata(account_xdata(*right));
+ if (! (rxdata.dflags & ACCOUNT_SORT_CALC)) {
+ sort_order->compute(rxdata.sort_value, details_t(*right));
+ rxdata.dflags |= ACCOUNT_SORT_CALC;
+ }
+
+ return lxdata.sort_value < rxdata.sort_value;
+}
+
+account_xdata_t& account_xdata(const account_t& account)
+{
+ if (! account.data) {
+ accounts_xdata.push_back(account_xdata_t());
+ account.data = &accounts_xdata.back();
+ accounts_xdata_ptrs.push_back(&account.data);
+ }
+ return *((account_xdata_t *) account.data);
+}
+
+void sum_accounts(account_t& account)
+{
+ account_xdata_t& xdata(account_xdata(account));
+
+ for (accounts_map::iterator i = account.accounts.begin();
+ i != account.accounts.end();
+ i++) {
+ sum_accounts(*(*i).second);
+
+ xdata.total += account_xdata_(*(*i).second).total;
+ xdata.total_count += (account_xdata_(*(*i).second).total_count +
+ account_xdata_(*(*i).second).count);
+ }
+
+ value_t result;
+ compute_amount(result, details_t(account));
+ if (result)
+ xdata.total += result;
+ xdata.total_count += xdata.count;
+}
+
+void sort_accounts(account_t& account,
+ const value_expr_t * sort_order,
+ accounts_deque& accounts)
+{
+ for (accounts_map::iterator i = account.accounts.begin();
+ i != account.accounts.end();
+ i++)
+ accounts.push_back((*i).second);
+
+ std::stable_sort(accounts.begin(), accounts.end(),
+ compare_items<account_t>(sort_order));
+}
+
+void walk_accounts(account_t& account,
+ item_handler<account_t>& handler,
+ const value_expr_t * sort_order)
+{
+ handler(account);
+
+ if (sort_order) {
+ accounts_deque accounts;
+ sort_accounts(account, sort_order, accounts);
+ for (accounts_deque::const_iterator i = accounts.begin();
+ i != accounts.end();
+ i++) {
+ account_xdata(**i).dflags &= ~ACCOUNT_SORT_CALC;
+ walk_accounts(**i, handler, sort_order);
+ }
+ } else {
+ for (accounts_map::const_iterator i = account.accounts.begin();
+ i != account.accounts.end();
+ i++)
+ walk_accounts(*(*i).second, handler, NULL);
+ }
+}
+
+void walk_accounts(account_t& account,
+ item_handler<account_t>& handler,
+ const std::string& sort_string)
+{
+ if (! sort_string.empty()) {
+ std::auto_ptr<value_expr_t> sort_order;
+ try {
+ sort_order.reset(parse_value_expr(sort_string));
+ }
+ catch (value_expr_error& err) {
+ throw error(std::string("In sort string '" + sort_string + "': " +
+ err.what()));
+ }
+ walk_accounts(account, handler, sort_order.get());
+ } else {
+ walk_accounts(account, handler);
+ }
+}
+
+void clear_accounts_xdata()
+{
+ accounts_xdata.clear();
+
+ for (std::list<void **>::iterator i = accounts_xdata_ptrs.begin();
+ i != accounts_xdata_ptrs.end();
+ i++)
+ **i = NULL;
+}
+
+
+void walk_commodities(commodities_map& commodities,
+ item_handler<transaction_t>& handler)
+{
+ std::list<transaction_t> xact_temps;
+ std::list<entry_t> entry_temps;
+ std::list<account_t> acct_temps;
+
+ for (commodities_map::iterator i = commodities.begin();
+ i != commodities.end();
+ i++) {
+ if ((*i).second->flags & COMMODITY_STYLE_NOMARKET)
+ continue;
+
+ entry_temps.push_back(entry_t());
+ acct_temps.push_back(account_t(NULL, (*i).second->symbol));
+
+ if ((*i).second->history)
+ for (history_map::iterator j = (*i).second->history->prices.begin();
+ j != (*i).second->history->prices.end();
+ j++) {
+ entry_temps.back().date = (*j).first;
+
+ xact_temps.push_back(transaction_t(&acct_temps.back()));
+ transaction_t& temp = xact_temps.back();
+ temp.entry = &entry_temps.back();
+ temp.amount = (*j).second;
+ temp.flags |= TRANSACTION_BULK_ALLOC;
+ entry_temps.back().add_transaction(&temp);
+
+ handler(xact_temps.back());
+ }
+ }
+
+ handler.flush();
+
+ clear_entries_transactions(entry_temps);
+}
+
+} // namespace ledger
+
+#ifdef USE_BOOST_PYTHON
+
+#include <boost/python.hpp>
+
+using namespace boost::python;
+using namespace ledger;
+
+template <typename T>
+struct item_handler_wrap : public item_handler<T>
+{
+ PyObject * self;
+
+ item_handler_wrap(PyObject * self_) : self(self_) {}
+ item_handler_wrap(PyObject * self_, const item_handler<T>& _handler)
+ : item_handler<T>(const_cast<item_handler<T> *>(&_handler)),
+ self(self_) {}
+ item_handler_wrap(PyObject * self_, item_handler<T> * _handler)
+ : item_handler<T>(_handler), self(self_) {}
+
+ virtual void flush() {
+ call_method<void>(self, "flush");
+ }
+ void default_flush() {
+ item_handler<T>::flush();
+ }
+
+ virtual void operator()(T& item) {
+ call_method<void>(self, "__call__", ptr(&item));
+ }
+ void default_call(T& item) {
+ item_handler<T>::operator()(item);
+ }
+};
+
+void (subtotal_transactions::*subtotal_transactions_flush)() =
+ &subtotal_transactions::flush;
+
+void py_walk_entries(journal_t& journal,
+ item_handler<transaction_t>& handler) {
+ walk_entries(journal.entries, handler);
+}
+
+void py_walk_transactions(entry_t& entry,
+ item_handler<transaction_t>& handler) {
+ walk_transactions(entry.transactions, handler);
+}
+
+void py_walk_accounts_1(account_t& account,
+ item_handler<account_t>& handler) {
+ walk_accounts(account, handler);
+}
+
+void py_walk_accounts_2(account_t& account,
+ item_handler<account_t>& handler,
+ const value_expr_t * sort_order) {
+ walk_accounts(account, handler, sort_order);
+}
+
+void py_walk_accounts_3(account_t& account,
+ item_handler<account_t>& handler,
+ const std::string& sort_string) {
+ walk_accounts(account, handler, sort_string);
+}
+
+void py_walk_commodities(item_handler<transaction_t>& handler) {
+ walk_commodities(commodity_t::commodities, handler);
+}
+
+void py_add_period_entries(generate_transactions& handler,
+ journal_t * journal) {
+ handler.add_period_entries(journal->period_entries);
+}
+
+void export_walk()
+{
+ typedef item_handler<transaction_t> xact_handler_t;
+
+ scope().attr("TRANSACTION_RECEIVED") = TRANSACTION_RECEIVED;
+ scope().attr("TRANSACTION_HANDLED") = TRANSACTION_HANDLED;
+ scope().attr("TRANSACTION_TO_DISPLAY") = TRANSACTION_TO_DISPLAY;
+ scope().attr("TRANSACTION_DISPLAYED") = TRANSACTION_DISPLAYED;
+ scope().attr("TRANSACTION_NO_TOTAL") = TRANSACTION_NO_TOTAL;
+ scope().attr("TRANSACTION_SORT_CALC") = TRANSACTION_SORT_CALC;
+ scope().attr("TRANSACTION_COMPOSITE") = TRANSACTION_COMPOSITE;
+ scope().attr("TRANSACTION_MATCHES") = TRANSACTION_MATCHES;
+
+ class_< transaction_xdata_t > ("TransactionXData")
+ .def_readwrite("total", &transaction_xdata_t::total)
+ .def_readwrite("sort_value", &transaction_xdata_t::sort_value)
+ .def_readwrite("composite_amount", &transaction_xdata_t::composite_amount)
+ .def_readwrite("index", &transaction_xdata_t::index)
+ .def_readwrite("dflags", &transaction_xdata_t::dflags)
+ ;
+
+ def("transaction_has_xdata", transaction_has_xdata);
+ def("transaction_xdata", transaction_xdata, return_internal_reference<1>());
+ def("clear_transactions_xdata", clear_transactions_xdata);
+ def("add_transaction_to", add_transaction_to);
+
+ class_< xact_handler_t, item_handler_wrap<transaction_t> >
+ ("TransactionHandler")
+ .def(init<xact_handler_t *>()[with_custodian_and_ward<1, 2>()])
+
+ .def("flush", &xact_handler_t::flush,
+ &item_handler_wrap<transaction_t>::default_flush)
+ .def("__call__", &xact_handler_t::operator(),
+ &item_handler_wrap<transaction_t>::default_call)
+ ;
+
+ class_< ignore_transactions, bases<xact_handler_t> >
+ ("IgnoreTransactions")
+ .def("flush", &xact_handler_t::flush)
+ .def("__call__", &ignore_transactions::operator());
+ ;
+
+ class_< set_account_value, bases<xact_handler_t> > ("SetAccountValue")
+ .def(init<xact_handler_t *>()[with_custodian_and_ward<1, 2>()])
+ .def("flush", &xact_handler_t::flush)
+ .def("__call__", &set_account_value::operator());
+ ;
+
+ class_< sort_transactions, bases<xact_handler_t> >
+ ("SortTransactions", init<xact_handler_t *, const value_expr_t *>()
+ [with_custodian_and_ward<1, 2>()])
+ .def(init<xact_handler_t *, const std::string&>()
+ [with_custodian_and_ward<1, 2>()])
+ .def("flush", &sort_transactions::flush)
+ .def("__call__", &sort_transactions::operator());
+ ;
+
+ class_< filter_transactions, bases<xact_handler_t> >
+ ("FilterTransactions", init<xact_handler_t *, std::string>()
+ [with_custodian_and_ward<1, 2>()])
+ .def(init<xact_handler_t *, const std::string&>()
+ [with_custodian_and_ward<1, 2>()])
+ .def("flush", &xact_handler_t::flush)
+ .def("__call__", &filter_transactions::operator());
+ ;
+
+ class_< calc_transactions, bases<xact_handler_t> >
+ ("CalcTransactions", init<xact_handler_t *>()
+ [with_custodian_and_ward<1, 2>()])
+ .def("flush", &xact_handler_t::flush)
+ .def("__call__", &calc_transactions::operator());
+ ;
+
+ class_< invert_transactions, bases<xact_handler_t> >
+ ("InvertTransactions", init<xact_handler_t *>()
+ [with_custodian_and_ward<1, 2>()])
+ .def("flush", &xact_handler_t::flush)
+ .def("__call__", &invert_transactions::operator());
+ ;
+
+ class_< collapse_transactions, bases<xact_handler_t> >
+ ("CollapseTransactions", init<xact_handler_t *>()
+ [with_custodian_and_ward<1, 2>()])
+ .def("flush", &collapse_transactions::flush)
+ .def("__call__", &collapse_transactions::operator());
+ ;
+
+ class_< related_transactions, bases<xact_handler_t> >
+ ("RelatedTransactions", init<xact_handler_t *, optional<bool> >()
+ [with_custodian_and_ward<1, 2>()])
+ .def("flush", &xact_handler_t::flush)
+ .def("__call__", &related_transactions::operator());
+ ;
+
+ class_< changed_value_transactions, bases<xact_handler_t> >
+ ("ChangeValueTransactions", init<xact_handler_t *, bool>()
+ [with_custodian_and_ward<1, 2>()])
+ .def("flush", &changed_value_transactions::flush)
+ .def("__call__", &changed_value_transactions::operator());
+ ;
+
+ class_< subtotal_transactions, bases<xact_handler_t> >
+ ("SubtotalTransactions", init<xact_handler_t *>()
+ [with_custodian_and_ward<1, 2>()])
+ .def("flush", subtotal_transactions_flush)
+ .def("__call__", &subtotal_transactions::operator());
+ ;
+
+ class_< interval_transactions, bases<xact_handler_t> >
+ ("IntervalTransactions",
+ init<xact_handler_t *, interval_t, optional<value_expr_t *> >()
+ [with_custodian_and_ward<1, 2>()])
+ .def(init<xact_handler_t *, const std::string&,
+ optional<const std::string&> >()
+ [with_custodian_and_ward<1, 2>()])
+ .def("flush", &xact_handler_t::flush)
+ .def("__call__", &interval_transactions::operator());
+ ;
+
+ class_< by_payee_transactions, bases<xact_handler_t> >
+ ("ByPayeeTransactions", init<xact_handler_t *>()
+ [with_custodian_and_ward<1, 2>()])
+ .def("flush", &by_payee_transactions::flush)
+ .def("__call__", &by_payee_transactions::operator());
+ ;
+
+ class_< set_comm_as_payee, bases<xact_handler_t> >
+ ("SetCommAsPayee", init<xact_handler_t *>()
+ [with_custodian_and_ward<1, 2>()])
+ .def("flush", &xact_handler_t::flush)
+ .def("__call__", &xact_handler_t::operator());
+ ;
+
+ class_< dow_transactions, bases<xact_handler_t> >
+ ("DowTransactions", init<xact_handler_t *>()
+ [with_custodian_and_ward<1, 2>()])
+ .def("flush", &dow_transactions::flush)
+ .def("__call__", &dow_transactions::operator());
+ ;
+
+ scope().attr("BUDGET_BUDGETED") = BUDGET_BUDGETED;
+ scope().attr("BUDGET_UNBUDGETED") = BUDGET_UNBUDGETED;
+
+ class_< generate_transactions, bases<xact_handler_t> >
+ ("GenerateTransactions", init<xact_handler_t *>()
+ [with_custodian_and_ward<1, 2>()])
+ .def("add_transaction", &generate_transactions::add_transaction)
+ .def("add_period_entries", py_add_period_entries)
+ .def("flush", &xact_handler_t::flush)
+ .def("__call__", &xact_handler_t::operator());
+ ;
+
+ class_< budget_transactions, bases<generate_transactions> >
+ ("BudgetTransactions",
+ init<xact_handler_t *, unsigned long>()
+ [with_custodian_and_ward<1, 2>()])
+ .def("add_transaction", &generate_transactions::add_transaction)
+ .def("add_period_entries", py_add_period_entries)
+ .def("flush", &budget_transactions::flush)
+ .def("__call__", &xact_handler_t::operator());
+ ;
+
+ class_< forecast_transactions, bases<generate_transactions> >
+ ("ForecastTransactions",
+ init<xact_handler_t *, std::string>()
+ [with_custodian_and_ward<1, 2>()])
+ .def("add_transaction", &forecast_transactions::add_transaction)
+ .def("add_period_entries", py_add_period_entries)
+ .def("flush", &forecast_transactions::flush)
+ .def("__call__", &xact_handler_t::operator());
+ ;
+
+ def("walk_entries", py_walk_entries);
+ def("walk_transactions", py_walk_transactions);
+
+ typedef item_handler<account_t> account_handler_t;
+
+ scope().attr("ACCOUNT_TO_DISPLAY") = ACCOUNT_TO_DISPLAY;
+ scope().attr("ACCOUNT_DISPLAYED") = ACCOUNT_DISPLAYED;
+ scope().attr("ACCOUNT_SORT_CALC") = ACCOUNT_SORT_CALC;
+
+ class_< account_xdata_t > ("AccountXData")
+ .def_readwrite("value", &account_xdata_t::value)
+ .def_readwrite("total", &account_xdata_t::total)
+ .def_readwrite("sort_value", &account_xdata_t::sort_value)
+ .def_readwrite("count", &account_xdata_t::count)
+ .def_readwrite("total_count", &account_xdata_t::total_count)
+ .def_readwrite("virtuals", &account_xdata_t::virtuals)
+ .def_readwrite("dflags", &account_xdata_t::dflags)
+ ;
+
+ def("account_has_xdata", account_has_xdata);
+ def("account_xdata", account_xdata, return_internal_reference<1>());
+ def("clear_accounts_xdata", clear_accounts_xdata);
+
+ class_< account_handler_t, item_handler_wrap<account_t> >
+ ("AccountHandler")
+ .def(init<account_handler_t *>()[with_custodian_and_ward<1, 2>()])
+
+ .def("flush", &account_handler_t::flush,
+ &item_handler_wrap<account_t>::default_flush)
+ .def("__call__", &account_handler_t::operator(),
+ &item_handler_wrap<account_t>::default_call)
+ ;
+
+ def("sum_accounts", sum_accounts);
+ def("walk_accounts", py_walk_accounts_1);
+ def("walk_accounts", py_walk_accounts_2);
+ def("walk_accounts", py_walk_accounts_3);
+
+ def("walk_commodities", py_walk_commodities);
+}
+
+#endif // USE_BOOST_PYTHON