#include "reconcile.h" #include "walk.h" namespace ledger { #define xact_next(x) ((transaction_t *)transaction_xdata(*x).ptr) #define xact_next_ptr(x) ((transaction_t **)&transaction_xdata(*x).ptr) static bool search_for_balance(amount_t& amount, transaction_t ** prev, transaction_t * next) { for (; next; next = xact_next(next)) { transaction_t * temp = *prev; *prev = next; amount -= next->amount; if (! amount || search_for_balance(amount, xact_next_ptr(next), xact_next(next))) return true; amount += next->amount; *prev = temp; } return false; } void reconcile_transactions::push_to_handler(transaction_t * first) { for (; first; first = xact_next(first)) item_handler::operator()(*first); item_handler::flush(); } void reconcile_transactions::flush() { value_t cleared_balance; value_t pending_balance; transaction_t * first = NULL; transaction_t ** last_ptr = &first; bool found_pending = false; for (transactions_list::iterator x = xacts.begin(); x != xacts.end(); x++) if (! cutoff || std::difftime((*x)->entry->date, cutoff) < 0) switch ((*x)->entry->state) { case entry_t::CLEARED: cleared_balance += (*x)->amount; if (! found_pending) break; // fall through... case entry_t::UNCLEARED: case entry_t::PENDING: pending_balance += (*x)->amount; if (all_pending) found_pending = true; *last_ptr = *x; last_ptr = xact_next_ptr(*x); break; } if (all_pending) { push_to_handler(first); return; } if (cleared_balance.type >= value_t::BALANCE) throw error("Cannot reconcile accounts with multiple commodities"); cleared_balance.cast(value_t::AMOUNT); balance.cast(value_t::AMOUNT); commodity_t& cb_comm = ((amount_t *) cleared_balance.data)->commodity(); commodity_t& b_comm = ((amount_t *) balance.data)->commodity(); balance -= cleared_balance; if (balance.type >= value_t::BALANCE) throw error(std::string("Reconcile balance is not of the same commodity ('") + b_comm.symbol + "' != '" + cb_comm.symbol + "')"); // If the amount to reconcile is the same as the pending balance, // then assume an exact match and return the results right away. amount_t to_reconcile = *((amount_t *) balance.data); pending_balance.cast(value_t::AMOUNT); if (to_reconcile == *((amount_t *) pending_balance.data) || search_for_balance(to_reconcile, &first, first)) { push_to_handler(first); } else { throw error("Could not reconcile account!"); } } } // namespace ledger #ifdef USE_BOOST_PYTHON #include using namespace boost::python; using namespace ledger; void export_reconcile() { class_< reconcile_transactions, bases > > ("ReconcileTransactions", init *, const value_t&, time_t, bool>() [with_custodian_and_ward<1, 2>()]) .def("flush", &reconcile_transactions::flush) .def("__call__", &reconcile_transactions::operator()) ; } #endif // USE_BOOST_PYTHON