greatly improved the flexibility of the "entry" command; no more bogus 1-liners
This commit is contained in:
parent
8a7cee24c8
commit
d0ba09f1e0
4 changed files with 91 additions and 35 deletions
3
NEWS
3
NEWS
|
|
@ -90,6 +90,9 @@
|
||||||
- The use of "+" and "-" in ledger files (to specify permanent
|
- The use of "+" and "-" in ledger files (to specify permanent
|
||||||
regexps) has been removed.
|
regexps) has been removed.
|
||||||
|
|
||||||
|
- The "-from" argument is no longer needed with the "entry" command.
|
||||||
|
It can simply be removed, with no other changes.
|
||||||
|
|
||||||
- -l now takes a value expression as the "calculation predicate".
|
- -l now takes a value expression as the "calculation predicate".
|
||||||
To mimic the old behavior of "-l \$100", use: -d "T&AT>{\$100}"
|
To mimic the old behavior of "-l \$100", use: -d "T&AT>{\$100}"
|
||||||
|
|
||||||
|
|
|
||||||
98
journal.cc
98
journal.cc
|
|
@ -1,6 +1,7 @@
|
||||||
#include "ledger.h"
|
#include "ledger.h"
|
||||||
#include "valexpr.h"
|
#include "valexpr.h"
|
||||||
#include "datetime.h"
|
#include "datetime.h"
|
||||||
|
#include "error.h"
|
||||||
#include "acconf.h"
|
#include "acconf.h"
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
@ -191,6 +192,26 @@ account_t * account_t::find_account(const std::string& name,
|
||||||
return account;
|
return account;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline
|
||||||
|
account_t * find_account_re_(account_t * account, const mask_t& regexp)
|
||||||
|
{
|
||||||
|
if (regexp.match(account->fullname()))
|
||||||
|
return account;
|
||||||
|
|
||||||
|
for (accounts_map::iterator i = account->accounts.begin();
|
||||||
|
i != account->accounts.end();
|
||||||
|
i++)
|
||||||
|
if (account_t * a = find_account_re_((*i).second, regexp))
|
||||||
|
return a;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
account_t * journal_t::find_account_re(const std::string& regexp)
|
||||||
|
{
|
||||||
|
return find_account_re_(master, mask_t(regexp));
|
||||||
|
}
|
||||||
|
|
||||||
bool account_t::remove_transaction(transaction_t * xact)
|
bool account_t::remove_transaction(transaction_t * xact)
|
||||||
{
|
{
|
||||||
transactions.remove(xact);
|
transactions.remove(xact);
|
||||||
|
|
@ -314,7 +335,7 @@ bool journal_t::remove_entry(entry_t * entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
entry_t * journal_t::derive_entry(strings_list::iterator i,
|
entry_t * journal_t::derive_entry(strings_list::iterator i,
|
||||||
strings_list::iterator end) const
|
strings_list::iterator end)
|
||||||
{
|
{
|
||||||
std::auto_ptr<entry_t> added(new entry_t);
|
std::auto_ptr<entry_t> added(new entry_t);
|
||||||
|
|
||||||
|
|
@ -328,7 +349,7 @@ entry_t * journal_t::derive_entry(strings_list::iterator i,
|
||||||
|
|
||||||
mask_t regexp(*i++);
|
mask_t regexp(*i++);
|
||||||
|
|
||||||
for (entries_list::const_reverse_iterator j = entries.rbegin();
|
for (entries_list::reverse_iterator j = entries.rbegin();
|
||||||
j != entries.rend();
|
j != entries.rend();
|
||||||
j++)
|
j++)
|
||||||
if (regexp.match((*j)->payee)) {
|
if (regexp.match((*j)->payee)) {
|
||||||
|
|
@ -343,13 +364,12 @@ entry_t * journal_t::derive_entry(strings_list::iterator i,
|
||||||
|
|
||||||
if ((*i)[0] == '-' || std::isdigit((*i)[0])) {
|
if ((*i)[0] == '-' || std::isdigit((*i)[0])) {
|
||||||
if (! matching)
|
if (! matching)
|
||||||
throw error("Missing account name for non-matching entry");
|
throw error("Could not determine the account to draw from");
|
||||||
|
|
||||||
transaction_t * m_xact, * xact, * first;
|
transaction_t * m_xact, * xact, * first;
|
||||||
m_xact = matching->transactions.front();
|
m_xact = matching->transactions.front();
|
||||||
|
|
||||||
amount_t amt(*i++);
|
first = xact = new transaction_t(m_xact->account, amount_t(*i++));
|
||||||
first = xact = new transaction_t(m_xact->account, amt);
|
|
||||||
added->add_transaction(xact);
|
added->add_transaction(xact);
|
||||||
|
|
||||||
if (xact->amount.commodity().symbol.empty())
|
if (xact->amount.commodity().symbol.empty())
|
||||||
|
|
@ -360,18 +380,19 @@ entry_t * journal_t::derive_entry(strings_list::iterator i,
|
||||||
xact = new transaction_t(m_xact->account, - first->amount);
|
xact = new transaction_t(m_xact->account, - first->amount);
|
||||||
added->add_transaction(xact);
|
added->add_transaction(xact);
|
||||||
|
|
||||||
if (i != end && std::string(*i++) == "-from" && i != end)
|
if (i != end)
|
||||||
if (account_t * acct = find_account(*i))
|
if (account_t * acct = find_account(*i))
|
||||||
added->transactions.back()->account = acct;
|
added->transactions.back()->account = acct;
|
||||||
} else {
|
} else {
|
||||||
while (std::string(*i) != "-from") {
|
while (i != end) {
|
||||||
mask_t acct_regex(*i++);
|
std::string& re_pat(*i++);
|
||||||
|
|
||||||
account_t * acct = NULL;
|
account_t * acct = NULL;
|
||||||
commodity_t * cmdty = NULL;
|
commodity_t * cmdty = NULL;
|
||||||
|
|
||||||
if (matching) {
|
if (matching) {
|
||||||
for (transactions_list::iterator x
|
mask_t acct_regex(re_pat);
|
||||||
|
|
||||||
|
for (transactions_list::const_iterator x
|
||||||
= matching->transactions.begin();
|
= matching->transactions.begin();
|
||||||
x != matching->transactions.end();
|
x != matching->transactions.end();
|
||||||
x++) {
|
x++) {
|
||||||
|
|
@ -383,38 +404,37 @@ entry_t * journal_t::derive_entry(strings_list::iterator i,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! acct)
|
if (! acct) {
|
||||||
acct = find_account(acct_regex.pattern);
|
acct = find_account_re(re_pat);
|
||||||
|
if (acct && acct->transactions.size() > 0)
|
||||||
|
cmdty = &acct->transactions.back()->amount.commodity();
|
||||||
|
}
|
||||||
|
|
||||||
if (! acct)
|
if (! acct)
|
||||||
throw error(std::string("Could not find account name '") +
|
acct = find_account(re_pat);
|
||||||
acct_regex.pattern + "'");
|
|
||||||
|
|
||||||
if (i == end)
|
if (i == end) {
|
||||||
throw error("Too few arguments to 'entry'");
|
added->add_transaction(new transaction_t(acct));
|
||||||
|
goto done;
|
||||||
amount_t amt(*i++);
|
}
|
||||||
transaction_t * xact = new transaction_t(acct, amt);
|
|
||||||
added->add_transaction(xact);
|
|
||||||
|
|
||||||
|
transaction_t * xact = new transaction_t(acct, amount_t(*i++));
|
||||||
if (! xact->amount.commodity())
|
if (! xact->amount.commodity())
|
||||||
xact->amount.set_commodity(*cmdty);
|
xact->amount.set_commodity(*cmdty);
|
||||||
|
|
||||||
|
added->add_transaction(xact);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i != end && std::string(*i++) == "-from" && i != end) {
|
if (! matching) {
|
||||||
if (account_t * acct = find_account(*i++))
|
throw error("Could not determine the account to draw from");
|
||||||
added->add_transaction(new transaction_t(acct));
|
} else {
|
||||||
}
|
|
||||||
else if (! matching) {
|
|
||||||
throw error("Could not figure out the account to draw from");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
transaction_t * xact
|
transaction_t * xact
|
||||||
= new transaction_t(matching->transactions.back()->account);
|
= new transaction_t(matching->transactions.back()->account);
|
||||||
added->add_transaction(xact);
|
added->add_transaction(xact);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
return added.release();
|
return added.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -443,6 +463,7 @@ bool journal_t::valid() const
|
||||||
#ifdef USE_BOOST_PYTHON
|
#ifdef USE_BOOST_PYTHON
|
||||||
|
|
||||||
#include <boost/python.hpp>
|
#include <boost/python.hpp>
|
||||||
|
#include <boost/python/exception_translator.hpp>
|
||||||
|
|
||||||
using namespace boost::python;
|
using namespace boost::python;
|
||||||
using namespace ledger;
|
using namespace ledger;
|
||||||
|
|
@ -565,6 +586,17 @@ account_t * py_find_account_2(journal_t& journal, const std::string& name,
|
||||||
return journal.find_account(name, auto_create);
|
return journal.find_account(name, auto_create);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define EXC_TRANSLATOR(type) \
|
||||||
|
void exc_translate_ ## type(const type& err) { \
|
||||||
|
PyErr_SetString(PyExc_RuntimeError, err.what()); \
|
||||||
|
}
|
||||||
|
|
||||||
|
EXC_TRANSLATOR(error)
|
||||||
|
EXC_TRANSLATOR(compute_error)
|
||||||
|
EXC_TRANSLATOR(value_expr_error)
|
||||||
|
EXC_TRANSLATOR(interval_expr_error)
|
||||||
|
EXC_TRANSLATOR(format_error)
|
||||||
|
EXC_TRANSLATOR(parse_error)
|
||||||
|
|
||||||
void export_journal()
|
void export_journal()
|
||||||
{
|
{
|
||||||
|
|
@ -669,6 +701,16 @@ void export_journal()
|
||||||
.value("CLEARED", entry_t::CLEARED)
|
.value("CLEARED", entry_t::CLEARED)
|
||||||
.value("PENDING", entry_t::PENDING)
|
.value("PENDING", entry_t::PENDING)
|
||||||
;
|
;
|
||||||
|
|
||||||
|
#define EXC_TRANSLATE(type) \
|
||||||
|
register_exception_translator<type>(&exc_translate_ ## type);
|
||||||
|
|
||||||
|
EXC_TRANSLATE(error);
|
||||||
|
EXC_TRANSLATE(compute_error);
|
||||||
|
EXC_TRANSLATE(value_expr_error);
|
||||||
|
EXC_TRANSLATE(interval_expr_error);
|
||||||
|
EXC_TRANSLATE(format_error);
|
||||||
|
EXC_TRANSLATE(parse_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // USE_BOOST_PYTHON
|
#endif // USE_BOOST_PYTHON
|
||||||
|
|
|
||||||
8
ledger.h
8
ledger.h
|
|
@ -224,17 +224,13 @@ class journal_t
|
||||||
accounts_cache.insert(accounts_pair(name, account));
|
accounts_cache.insert(accounts_pair(name, account));
|
||||||
return account;
|
return account;
|
||||||
}
|
}
|
||||||
account_t * find_account(const std::string& name) const {
|
account_t * find_account_re(const std::string& regexp);
|
||||||
// With auto_create false, the other `find_account' will not
|
|
||||||
// change the object.
|
|
||||||
return const_cast<journal_t *>(this)->find_account(name, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool add_entry(entry_t * entry);
|
bool add_entry(entry_t * entry);
|
||||||
bool remove_entry(entry_t * entry);
|
bool remove_entry(entry_t * entry);
|
||||||
|
|
||||||
entry_t * derive_entry(strings_list::iterator begin,
|
entry_t * derive_entry(strings_list::iterator begin,
|
||||||
strings_list::iterator end) const;
|
strings_list::iterator end);
|
||||||
|
|
||||||
bool valid() const;
|
bool valid() const;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
17
ledger.texi
17
ledger.texi
|
|
@ -294,7 +294,22 @@ exit code to 1.
|
||||||
|
|
||||||
There is a shell script in the distribution called ``entry'', which
|
There is a shell script in the distribution called ``entry'', which
|
||||||
simplifies the task of adding a new entry to your ledger, and then
|
simplifies the task of adding a new entry to your ledger, and then
|
||||||
launches @command{vi} to let you confirm that the entry looks appropriate.
|
launches @command{vi} to let you confirm that the entry looks
|
||||||
|
appropriate.
|
||||||
|
|
||||||
|
Here are a few more examples of the entry command, assuming the above
|
||||||
|
journal entry:
|
||||||
|
|
||||||
|
@example
|
||||||
|
# Pay $11.00 to first transaction account (food)
|
||||||
|
ledger entry 4/9 viva 11.00
|
||||||
|
|
||||||
|
# Pay $11.00 to food from checking
|
||||||
|
ledger entry 4/9 viva 11.00 checking
|
||||||
|
|
||||||
|
# Pay DM 11.00 to dining, no matter what the entry said
|
||||||
|
ledger entry 4/9 viva dining "DM 11.00"
|
||||||
|
@end example
|
||||||
|
|
||||||
@node Options, Format strings, Commands, Running Ledger
|
@node Options, Format strings, Commands, Running Ledger
|
||||||
@section Options
|
@section Options
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue