848 lines
21 KiB
C++
848 lines
21 KiB
C++
#include "walk.h"
|
|
#include "format.h"
|
|
#include "textual.h"
|
|
#include "util.h"
|
|
|
|
#include <algorithm>
|
|
|
|
namespace ledger {
|
|
|
|
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)
|
|
xact.data = new transaction_xdata_t();
|
|
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 truncate_entries::flush()
|
|
{
|
|
if (! xacts.size())
|
|
return;
|
|
|
|
entry_t * last_entry = (*xacts.begin())->entry;
|
|
|
|
int l = 0;
|
|
for (transactions_list::iterator x = xacts.begin();
|
|
x != xacts.end();
|
|
x++)
|
|
if (last_entry != (*x)->entry) {
|
|
l++;
|
|
last_entry = (*x)->entry;
|
|
}
|
|
l++;
|
|
|
|
last_entry = (*xacts.begin())->entry;
|
|
|
|
int i = 0;
|
|
for (transactions_list::iterator x = xacts.begin();
|
|
x != xacts.end();
|
|
x++) {
|
|
if (last_entry != (*x)->entry) {
|
|
last_entry = (*x)->entry;
|
|
i++;
|
|
}
|
|
|
|
bool print = false;
|
|
if (head_count) {
|
|
if (head_count > 0 && i < head_count)
|
|
print = true;
|
|
else if (head_count < 0 && i >= - head_count)
|
|
print = true;
|
|
}
|
|
|
|
if (! print && tail_count) {
|
|
if (tail_count > 0 && l - i <= tail_count)
|
|
print = true;
|
|
else if (tail_count < 0 && l - i > - tail_count)
|
|
print = true;
|
|
}
|
|
|
|
if (print)
|
|
item_handler<transaction_t>::operator()(**x);
|
|
}
|
|
xacts.clear();
|
|
|
|
item_handler<transaction_t>::flush();
|
|
}
|
|
|
|
void set_account_value::operator()(transaction_t& xact)
|
|
{
|
|
account_t * acct = xact_account(xact);
|
|
assert(acct);
|
|
|
|
account_xdata_t& xdata = account_xdata(*acct);
|
|
add_transaction_to(xact, xdata.value);
|
|
|
|
xdata.count++;
|
|
if (xact.flags & TRANSACTION_VIRTUAL)
|
|
xdata.virtuals++;
|
|
|
|
item_handler<transaction_t>::operator()(xact);
|
|
}
|
|
|
|
void sort_transactions::post_accumulated_xacts()
|
|
{
|
|
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);
|
|
}
|
|
|
|
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++) {
|
|
if ((*i)->entry) {
|
|
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);
|
|
}
|
|
}
|
|
} else {
|
|
// This code should only be reachable from the "output"
|
|
// command, since that is the only command which attempts to
|
|
// output auto or period entries.
|
|
transaction_xdata_t& xdata = transaction_xdata(**i);
|
|
if (! (xdata.dflags & TRANSACTION_HANDLED) &&
|
|
! ((*i)->flags & TRANSACTION_AUTO)) {
|
|
xdata.dflags |= TRANSACTION_HANDLED;
|
|
item_handler<transaction_t>::operator()(**i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
else
|
|
moment = xact.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 += datetime_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.value, (*i).second.account, &entry, 0,
|
|
xact_temps, *handler, finish);
|
|
|
|
values.clear();
|
|
}
|
|
|
|
void subtotal_transactions::operator()(transaction_t& xact)
|
|
{
|
|
if (! start || std::difftime(xact.date(), start) < 0)
|
|
start = xact.date();
|
|
if (! finish || std::difftime(xact.date(), finish) > 0)
|
|
finish = xact.date();
|
|
|
|
account_t * acct = xact_account(xact);
|
|
assert(acct);
|
|
|
|
values_map::iterator i = values.find(acct->fullname());
|
|
if (i == values.end()) {
|
|
value_t temp;
|
|
add_transaction_to(xact, temp);
|
|
values.insert(values_pair(acct->fullname(), acct_value_t(acct, temp)));
|
|
} else {
|
|
add_transaction_to(xact, (*i).second.value);
|
|
}
|
|
|
|
// 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(xact)).dflags |= ACCOUNT_HAS_NON_VIRTUALS;
|
|
else if (! (xact.flags & TRANSACTION_BALANCE))
|
|
account_xdata(*xact_account(xact)).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->date();
|
|
|
|
subtotal_transactions::report_subtotal();
|
|
|
|
if (sorter)
|
|
sorter->post_accumulated_xacts();
|
|
|
|
last_xact = NULL;
|
|
}
|
|
|
|
void interval_transactions::operator()(transaction_t& xact)
|
|
{
|
|
const std::time_t date = xact.date();
|
|
|
|
if ((interval.begin && std::difftime(date, interval.begin) < 0) ||
|
|
(interval.end && std::difftime(date, interval.end) >= 0))
|
|
return;
|
|
|
|
if (interval) {
|
|
if (! started) {
|
|
if (! interval.begin)
|
|
interval.start(date);
|
|
start = interval.begin;
|
|
started = true;
|
|
}
|
|
|
|
std::time_t quant = interval.increment(interval.begin);
|
|
if (std::difftime(date, quant) >= 0) {
|
|
if (last_xact)
|
|
report_subtotal(quant);
|
|
|
|
std::time_t temp;
|
|
while (std::difftime(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.date(), (*i).second->start) > 0)
|
|
(*i).second->start = xact.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.date();
|
|
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.state = xact.state;
|
|
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)
|
|
return;
|
|
|
|
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(xact)->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 | TRANSACTION_BULK_ALLOC;
|
|
temp.amount.negate();
|
|
entry.add_transaction(&temp);
|
|
|
|
begin = (*i).first.increment(begin);
|
|
|
|
item_handler<transaction_t>::operator()(temp);
|
|
|
|
reported = true;
|
|
}
|
|
}
|
|
} while (reported);
|
|
}
|
|
|
|
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(xact);
|
|
acct;
|
|
acct = acct->parent) {
|
|
if (acct == xact_account(*(*i).second)) {
|
|
xact_in_budget = true;
|
|
// Report the transaction as if it had occurred in the parent
|
|
// account.
|
|
if (xact_account(xact) != acct)
|
|
transaction_xdata(xact).account = acct;
|
|
goto handle;
|
|
}
|
|
}
|
|
|
|
handle:
|
|
if (xact_in_budget && flags & BUDGET_BUDGETED) {
|
|
report_budget_items(xact.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.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();
|
|
}
|
|
|
|
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)
|
|
account.data = new account_xdata_t();
|
|
|
|
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()) {
|
|
value_auto_ptr 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 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
|