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.
197 lines
4.8 KiB
C++
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
|