ledger/derive.cc
John Wiegley 99313ebc6c Revised the way that exceptions are thrown around. Instead of context being a
complicated string of pointers, it's now just a global block of text that gets
appended to as the error is being thrown up, and can be displayed at the catch
point if desired.  There are almost no cases where a thrown exception will not
result in an error message being displayed to the user.
2008-07-31 06:24:45 -04:00

204 lines
5 KiB
C++

#include "derive.h"
#include "session.h"
#include "walk.h"
namespace ledger {
entry_t * derive_new_entry(report_t& report,
strings_list::iterator i,
strings_list::iterator end)
{
session_t& session(report.session);
std::auto_ptr<entry_t> added(new entry_t);
entry_t * matching = NULL;
// jww (2008-04-20): Need to parse the string here
//added->_date = *i++;
added->_date = boost::posix_time::time_from_string(*i++);
if (i == end)
throw std::runtime_error("Too few arguments to 'entry'");
mask_t regexp(*i++);
journals_iterator iter(session);
entries_list::reverse_iterator j;
for (journal_t * journal = iter(); journal; journal = iter()) {
for (j = journal->entries.rbegin();
j != journal->entries.rend();
j++) {
if (regexp.match((*j)->payee)) {
matching = *j;
break;
}
}
if (matching) break;
}
added->payee = matching ? matching->payee : regexp.expr.str();
if (! matching) {
account_t * acct;
if (i == end || ((*i)[0] == '-' || std::isdigit((*i)[0]))) {
acct = session.find_account("Expenses");
}
else if (i != end) {
acct = session.find_account_re(*i);
if (! acct)
acct = session.find_account(*i);
assert(acct);
i++;
}
if (i == end) {
added->add_xact(new xact_t(acct));
} else {
xact_t * xact = new xact_t(acct, amount_t(*i++));
added->add_xact(xact);
if (! xact->amount.commodity()) {
// If the amount has no commodity, we can determine it given
// the account by creating a final for the account and then
// checking if it contains only a single commodity. An
// account to which only dollars are applied would imply that
// dollars are wanted now too.
report.sum_all_accounts();
value_t total = account_xdata(*acct).total;
if (total.is_type(value_t::AMOUNT))
xact->amount.set_commodity(total.as_amount().commodity());
}
}
acct = NULL;
if (i != end) {
if (! acct)
acct = session.find_account_re(*i);
if (! acct)
acct = session.find_account(*i);
}
if (! acct) {
if (matching && matching->journal->basket)
acct = matching->journal->basket;
else
acct = session.find_account("Equity");
}
added->add_xact(new xact_t(acct));
}
else if (i == end) {
// If no argument were given but the payee, assume the user wants
// to see the same xact as last time.
added->code = matching->code;
for (xacts_list::iterator k = matching->xacts.begin();
k != matching->xacts.end();
k++)
added->add_xact(new xact_t(**k));
}
else if ((*i)[0] == '-' || std::isdigit((*i)[0])) {
xact_t * m_xact, * xact, * first;
m_xact = matching->xacts.front();
first = xact = new xact_t(m_xact->account, amount_t(*i++));
added->add_xact(xact);
if (! xact->amount.commodity())
xact->amount.set_commodity(m_xact->amount.commodity());
m_xact = matching->xacts.back();
xact = new xact_t(m_xact->account, - first->amount);
added->add_xact(xact);
if (i != end) {
account_t * acct = session.find_account_re(*i);
if (! acct)
acct = session.find_account(*i);
assert(acct);
added->xacts.back()->account = acct;
}
}
else {
account_t * draw_acct = NULL;
while (i != end) {
string& re_pat(*i++);
account_t * acct = NULL;
amount_t * amt = NULL;
mask_t acct_regex(re_pat);
for (; j != matching->journal->entries.rend(); j++)
if (regexp.match((*j)->payee)) {
entry_t * entry = *j;
for (xacts_list::const_iterator x =
entry->xacts.begin();
x != entry->xacts.end();
x++)
if (acct_regex.match((*x)->account->fullname())) {
acct = (*x)->account;
amt = &(*x)->amount;
matching = entry;
goto found;
}
}
found:
xact_t * xact;
if (i == end) {
if (amt)
xact = new xact_t(acct, *amt);
else
xact = new xact_t(acct);
} else {
amount_t amount(*i++);
strings_list::iterator x = i;
if (i != end && ++x == end) {
draw_acct = session.find_account_re(*i);
if (! draw_acct)
draw_acct = session.find_account(*i);
i++;
}
if (! acct)
acct = session.find_account_re(re_pat);
if (! acct)
acct = session.find_account(re_pat);
xact = new xact_t(acct, amount);
if (! xact->amount.commodity()) {
if (amt)
xact->amount.set_commodity(amt->commodity());
else if (amount_t::current_pool->default_commodity)
xact->amount.set_commodity(*amount_t::current_pool->default_commodity);
}
}
added->add_xact(xact);
}
if (! draw_acct) {
assert(matching->xacts.back()->account);
draw_acct = matching->xacts.back()->account;
}
if (draw_acct)
added->add_xact(new xact_t(draw_acct));
}
if ((matching &&
! matching->journal->entry_finalize_hooks.run_hooks(*added, false)) ||
! added->finalize() ||
(matching &&
! matching->journal->entry_finalize_hooks.run_hooks(*added, true)))
throw std::runtime_error("Failed to finalize derived entry (check commodities)");
return added.release();
}
} // namespace ledger