ledger/derive.cc
John Wiegley 858978de89 Journal data structures now use date_t instead of datetime_t.
This means transactions can only have day-level granularity -- which has
always been the case from an data file point of view.  The advantage to this
restriction is that reports will now be immune from daylight savings related
bugs, where a transaction falls to the wrong side of a --monthly report, for
example.
2008-08-01 17:37:22 -04:00

197 lines
4.8 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;
added->_date = parse_date(*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;
foreach (xact_t * xact, matching->xacts)
added->add_xact(new xact_t(*xact));
}
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;
foreach (xact_t * xact, entry->xacts)
if (acct_regex.match(xact->account->fullname())) {
acct = xact->account;
amt = &xact->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