moved entry hooking mechanism to journal_t; further improvements to "entry"
This commit is contained in:
parent
02580c2efb
commit
c57bfb72c3
9 changed files with 191 additions and 147 deletions
24
autoxact.cc
24
autoxact.cc
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
namespace ledger {
|
||||
|
||||
void automated_transaction_t::extend_entry(entry_t * entry)
|
||||
void automated_transaction_t::extend_entry(entry_t& entry)
|
||||
{
|
||||
transactions_deque initial_xacts(entry->transactions.begin(),
|
||||
entry->transactions.end());
|
||||
transactions_deque initial_xacts(entry.transactions.begin(),
|
||||
entry.transactions.end());
|
||||
|
||||
for (transactions_deque::iterator i = initial_xacts.begin();
|
||||
i != initial_xacts.end();
|
||||
|
|
@ -23,13 +23,13 @@ void automated_transaction_t::extend_entry(entry_t * entry)
|
|||
transaction_t * xact
|
||||
= new transaction_t((*t)->account, amt,
|
||||
(*t)->flags | TRANSACTION_AUTO);
|
||||
entry->add_transaction(xact);
|
||||
entry.add_transaction(xact);
|
||||
}
|
||||
}
|
||||
|
||||
automated_transactions_t * current_auto_xacts = NULL;
|
||||
|
||||
bool handle_auto_xacts(entry_t * entry)
|
||||
bool handle_auto_xacts(entry_t& entry)
|
||||
{
|
||||
if (current_auto_xacts &&
|
||||
! current_auto_xacts->automated_transactions.empty())
|
||||
|
|
@ -39,3 +39,17 @@ bool handle_auto_xacts(entry_t * entry)
|
|||
}
|
||||
|
||||
} // namespace ledger
|
||||
|
||||
#ifdef USE_BOOST_PYTHON
|
||||
|
||||
#include <boost/python.hpp>
|
||||
#include <Python.h>
|
||||
|
||||
using namespace boost::python;
|
||||
using namespace ledger;
|
||||
|
||||
void export_autoxact() {
|
||||
def("handle_auto_xacts", handle_auto_xacts);
|
||||
}
|
||||
|
||||
#endif // USE_BOOST_PYTHON
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ public:
|
|||
delete *i;
|
||||
}
|
||||
|
||||
void extend_entry(entry_t * entry);
|
||||
void extend_entry(entry_t& entry);
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -63,7 +63,7 @@ public:
|
|||
delete *i;
|
||||
}
|
||||
|
||||
void extend_entry(entry_t * entry) {
|
||||
void extend_entry(entry_t& entry) {
|
||||
for (automated_transactions_deque::iterator i
|
||||
= automated_transactions.begin();
|
||||
i != automated_transactions.end();
|
||||
|
|
@ -90,7 +90,7 @@ public:
|
|||
|
||||
extern automated_transactions_t * current_auto_xacts;
|
||||
|
||||
bool handle_auto_xacts(entry_t * entry);
|
||||
bool handle_auto_xacts(entry_t& entry);
|
||||
|
||||
} // namespace ledger
|
||||
|
||||
|
|
|
|||
121
journal.cc
121
journal.cc
|
|
@ -120,6 +120,93 @@ bool entry_t::remove_transaction(transaction_t * xact)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool finalize_entry(entry_t& entry)
|
||||
{
|
||||
// Scan through and compute the total balance for the entry. This
|
||||
// is used for auto-calculating the value of entries with no cost,
|
||||
// and the per-unit price of unpriced commodities.
|
||||
|
||||
value_t balance;
|
||||
|
||||
bool no_amounts = true;
|
||||
for (transactions_list::const_iterator x = entry.transactions.begin();
|
||||
x != entry.transactions.end();
|
||||
x++)
|
||||
if (! ((*x)->flags & TRANSACTION_VIRTUAL) ||
|
||||
((*x)->flags & TRANSACTION_BALANCE)) {
|
||||
amount_t * p = (*x)->cost ? (*x)->cost : &(*x)->amount;
|
||||
if (*p) {
|
||||
if (no_amounts) {
|
||||
balance = *p;
|
||||
no_amounts = false;
|
||||
} else {
|
||||
balance += *p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If it's a null entry, then let the user have their fun
|
||||
if (no_amounts)
|
||||
return true;
|
||||
|
||||
// If one transaction of a two-line transaction is of a different
|
||||
// commodity than the others, and it has no per-unit price,
|
||||
// determine its price by dividing the unit count into the value of
|
||||
// the balance. This is done for the last eligible commodity.
|
||||
|
||||
if (balance.type == value_t::BALANCE &&
|
||||
((balance_t *) balance.data)->amounts.size() == 2)
|
||||
for (transactions_list::const_iterator x = entry.transactions.begin();
|
||||
x != entry.transactions.end();
|
||||
x++) {
|
||||
if ((*x)->cost || ((*x)->flags & TRANSACTION_VIRTUAL))
|
||||
continue;
|
||||
|
||||
for (amounts_map::const_iterator i
|
||||
= ((balance_t *) balance.data)->amounts.begin();
|
||||
i != ((balance_t *) balance.data)->amounts.end();
|
||||
i++)
|
||||
if ((*i).second.commodity() != (*x)->amount.commodity()) {
|
||||
assert((*x)->amount);
|
||||
balance -= (*x)->amount;
|
||||
assert(! (*x)->cost);
|
||||
(*x)->cost = new amount_t(- (*i).second);
|
||||
balance += *(*x)->cost;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Walk through each of the transactions, fixing up any that we
|
||||
// can, and performing any on-the-fly calculations.
|
||||
|
||||
bool empty_allowed = true;
|
||||
|
||||
for (transactions_list::const_iterator x = entry.transactions.begin();
|
||||
x != entry.transactions.end();
|
||||
x++) {
|
||||
if ((*x)->amount || ((*x)->flags & TRANSACTION_VIRTUAL))
|
||||
continue;
|
||||
|
||||
if (! empty_allowed || ! balance || balance.type != value_t::AMOUNT)
|
||||
return false;
|
||||
|
||||
empty_allowed = false;
|
||||
|
||||
// If one transaction gives no value at all -- and all the
|
||||
// rest are of the same commodity -- then its value is the
|
||||
// inverse of the computed value of the others.
|
||||
|
||||
(*x)->amount = *((amount_t *) balance.data);
|
||||
(*x)->amount.negate();
|
||||
|
||||
balance = 0U;
|
||||
}
|
||||
|
||||
return ! balance;
|
||||
}
|
||||
|
||||
bool entry_t::valid() const
|
||||
{
|
||||
if (! date || date == -1)
|
||||
|
|
@ -296,6 +383,11 @@ journal_t::~journal_t()
|
|||
|
||||
bool journal_t::add_entry(entry_t * entry)
|
||||
{
|
||||
if (! run_hooks(entry_finalize_hooks, *entry)) {
|
||||
delete entry;
|
||||
return false;
|
||||
}
|
||||
|
||||
entries.push_back(entry);
|
||||
|
||||
for (transactions_list::const_iterator i = entry->transactions.begin();
|
||||
|
|
@ -439,6 +531,9 @@ entry_t * journal_t::derive_entry(strings_list::iterator i,
|
|||
}
|
||||
|
||||
done:
|
||||
if (! run_hooks(entry_finalize_hooks, *added))
|
||||
return NULL;
|
||||
|
||||
return added.release();
|
||||
}
|
||||
|
||||
|
|
@ -590,6 +685,27 @@ account_t * py_find_account_2(journal_t& journal, const std::string& name,
|
|||
return journal.find_account(name, auto_create);
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
void py_add_entry_finalize_hook(journal_t& journal, object x)
|
||||
{
|
||||
add_hook(journal.entry_finalize_hooks,
|
||||
extract<journal_t::entry_finalize_hook_t>(x));
|
||||
}
|
||||
|
||||
void py_remove_entry_finalize_hook(journal_t& journal, object x)
|
||||
{
|
||||
remove_hook(journal.entry_finalize_hooks,
|
||||
extract<journal_t::entry_finalize_hook_t>(x));
|
||||
}
|
||||
|
||||
void py_run_entry_finalize_hooks(journal_t& journal, entry_t& entry)
|
||||
{
|
||||
run_hooks(journal.entry_finalize_hooks, entry);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#define EXC_TRANSLATOR(type) \
|
||||
void exc_translate_ ## type(const type& err) { \
|
||||
PyErr_SetString(PyExc_RuntimeError, err.what()); \
|
||||
|
|
@ -677,6 +793,11 @@ void export_journal()
|
|||
|
||||
.def("add_entry", &journal_t::add_entry)
|
||||
.def("remove_entry", &journal_t::remove_entry)
|
||||
#if 0
|
||||
.def("add_entry_finalize_hook", py_add_entry_finalize_hook)
|
||||
.def("remove_entry_finalize_hook", py_remove_entry_finalize_hook)
|
||||
.def("run_entry_finalize_hooks", py_run_entry_finalize_hooks)
|
||||
#endif
|
||||
.def("derive_entry", &journal_t::derive_entry,
|
||||
return_value_policy<manage_new_object>())
|
||||
|
||||
|
|
|
|||
20
ledger.el
20
ledger.el
|
|
@ -100,11 +100,10 @@
|
|||
(if (time-less-p moment date)
|
||||
(throw 'found t)))))))
|
||||
|
||||
(defun ledger-add-entry (entry)
|
||||
(defun ledger-add-entry (entry-text)
|
||||
(interactive
|
||||
(list (read-string "Entry: " (format-time-string "%Y/%m/%d "))))
|
||||
(let* ((args (mapcar 'shell-quote-argument (split-string entry)))
|
||||
(date (car args))
|
||||
(list (read-string "Entry: " (format-time-string "%Y/%m/"))))
|
||||
(let* ((date (car (split-string entry-text)))
|
||||
(insert-year t) exit-code)
|
||||
(if (string-match "\\([0-9]+\\)/\\([0-9]+\\)/\\([0-9]+\\)" date)
|
||||
(setq date (encode-time 0 0 0 (string-to-int (match-string 3 date))
|
||||
|
|
@ -117,7 +116,7 @@
|
|||
(save-excursion
|
||||
(insert
|
||||
(with-temp-buffer
|
||||
(setq exit-code (apply 'ledger-run-ledger "entry" args))
|
||||
(setq exit-code (ledger-run-ledger "entry" entry-text))
|
||||
(if (= 0 exit-code)
|
||||
(progn
|
||||
(goto-char (point-min))
|
||||
|
|
@ -125,8 +124,8 @@
|
|||
(if insert-year
|
||||
(buffer-string)
|
||||
(buffer-substring 5 (point-max))))
|
||||
(concat (if insert-year entry
|
||||
(substring entry 5)) "\n"))) "\n"))))
|
||||
(concat (if insert-year entry-text
|
||||
(substring entry-text 5)) "\n"))) "\n"))))
|
||||
|
||||
(defun ledger-expand-entry ()
|
||||
(interactive)
|
||||
|
|
@ -303,8 +302,11 @@
|
|||
|
||||
(defun ledger-run-ledger (&rest args)
|
||||
"run ledger with supplied arguments"
|
||||
(apply 'call-process ledger-binary-path nil t nil
|
||||
(append (list "-f" ledger-data-file) args)))
|
||||
(let ((command (mapconcat 'identity
|
||||
(append (list ledger-binary-path
|
||||
"-f" ledger-data-file) args) " ")))
|
||||
(insert (shell-command-to-string command)))
|
||||
0)
|
||||
|
||||
(provide 'ledger)
|
||||
|
||||
|
|
|
|||
31
ledger.h
31
ledger.h
|
|
@ -116,6 +116,8 @@ class entry_t
|
|||
bool valid() const;
|
||||
};
|
||||
|
||||
bool finalize_entry(entry_t& entry);
|
||||
|
||||
|
||||
typedef std::map<const std::string, account_t *> accounts_map;
|
||||
typedef std::pair<const std::string, account_t *> accounts_pair;
|
||||
|
|
@ -181,6 +183,30 @@ class account_t
|
|||
std::ostream& operator<<(std::ostream& out, const account_t& account);
|
||||
|
||||
|
||||
template <typename T>
|
||||
void add_hook(std::list<T>& list, T func, const bool prepend = false) {
|
||||
if (prepend)
|
||||
list.push_front(func);
|
||||
else
|
||||
list.push_back(func);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void remove_hook(std::list<T>& list, T func) {
|
||||
list.remove(func);
|
||||
}
|
||||
|
||||
template <typename T, typename Data>
|
||||
bool run_hooks(std::list<T>& list, Data& entry) {
|
||||
for (typename std::list<T>::const_iterator i = list.begin();
|
||||
i != list.end();
|
||||
i++)
|
||||
if (! (*i)(entry))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
typedef std::list<entry_t *> entries_list;
|
||||
typedef std::list<std::string> strings_list;
|
||||
|
||||
|
|
@ -195,9 +221,14 @@ class journal_t
|
|||
|
||||
mutable accounts_map accounts_cache;
|
||||
|
||||
typedef bool (*entry_finalize_hook_t)(entry_t& entry);
|
||||
|
||||
std::list<entry_finalize_hook_t> entry_finalize_hooks;
|
||||
|
||||
journal_t() {
|
||||
master = new account_t(NULL, "");
|
||||
item_pool = item_pool_end = NULL;
|
||||
add_hook(entry_finalize_hooks, finalize_entry);
|
||||
}
|
||||
~journal_t();
|
||||
|
||||
|
|
|
|||
3
main.cc
3
main.cc
|
|
@ -164,6 +164,8 @@ int parse_and_report(int argc, char * argv[], char * envp[])
|
|||
{
|
||||
std::auto_ptr<journal_t> journal(new journal_t);
|
||||
|
||||
add_hook(journal->entry_finalize_hooks, handle_auto_xacts);
|
||||
|
||||
// Parse command-line arguments, and those set in the environment
|
||||
|
||||
TIMER_START(process_opts);
|
||||
|
|
@ -224,7 +226,6 @@ int parse_and_report(int argc, char * argv[], char * envp[])
|
|||
#endif
|
||||
std::auto_ptr<qif_parser_t> qif_parser(new qif_parser_t);
|
||||
std::auto_ptr<textual_parser_t> text_parser(new textual_parser_t);
|
||||
text_parser->add_finalize_hook(handle_auto_xacts);
|
||||
|
||||
register_parser(bin_parser.get());
|
||||
#ifdef READ_GNUCASH
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ void export_walk();
|
|||
void export_format();
|
||||
void export_valexpr();
|
||||
void export_datetime();
|
||||
void export_autoxact();
|
||||
|
||||
void initialize_ledger_for_python()
|
||||
{
|
||||
|
|
@ -48,6 +49,7 @@ void initialize_ledger_for_python()
|
|||
export_format();
|
||||
export_valexpr();
|
||||
export_datetime();
|
||||
export_autoxact();
|
||||
|
||||
module_initialized = true;
|
||||
}
|
||||
|
|
|
|||
102
textual.cc
102
textual.cc
|
|
@ -145,93 +145,6 @@ void parse_automated_transactions(std::istream& in,
|
|||
add_automated_transaction(new automated_transaction_t(line + 1, xacts));
|
||||
}
|
||||
|
||||
bool finalize_entry(entry_t * entry)
|
||||
{
|
||||
// Scan through and compute the total balance for the entry. This
|
||||
// is used for auto-calculating the value of entries with no cost,
|
||||
// and the per-unit price of unpriced commodities.
|
||||
|
||||
value_t balance;
|
||||
|
||||
bool no_amounts = true;
|
||||
for (transactions_list::const_iterator x = entry->transactions.begin();
|
||||
x != entry->transactions.end();
|
||||
x++)
|
||||
if (! ((*x)->flags & TRANSACTION_VIRTUAL) ||
|
||||
((*x)->flags & TRANSACTION_BALANCE)) {
|
||||
amount_t * p = (*x)->cost ? (*x)->cost : &(*x)->amount;
|
||||
if (*p) {
|
||||
if (no_amounts) {
|
||||
balance = *p;
|
||||
no_amounts = false;
|
||||
} else {
|
||||
balance += *p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If it's a null entry, then let the user have their fun
|
||||
if (no_amounts)
|
||||
return true;
|
||||
|
||||
// If one transaction of a two-line transaction is of a different
|
||||
// commodity than the others, and it has no per-unit price,
|
||||
// determine its price by dividing the unit count into the value of
|
||||
// the balance. This is done for the last eligible commodity.
|
||||
|
||||
if (balance.type == value_t::BALANCE &&
|
||||
((balance_t *) balance.data)->amounts.size() == 2)
|
||||
for (transactions_list::const_iterator x = entry->transactions.begin();
|
||||
x != entry->transactions.end();
|
||||
x++) {
|
||||
if ((*x)->cost || ((*x)->flags & TRANSACTION_VIRTUAL))
|
||||
continue;
|
||||
|
||||
for (amounts_map::const_iterator i
|
||||
= ((balance_t *) balance.data)->amounts.begin();
|
||||
i != ((balance_t *) balance.data)->amounts.end();
|
||||
i++)
|
||||
if ((*i).second.commodity() != (*x)->amount.commodity()) {
|
||||
assert((*x)->amount);
|
||||
balance -= (*x)->amount;
|
||||
assert(! (*x)->cost);
|
||||
(*x)->cost = new amount_t(- (*i).second);
|
||||
balance += *(*x)->cost;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Walk through each of the transactions, fixing up any that we
|
||||
// can, and performing any on-the-fly calculations.
|
||||
|
||||
bool empty_allowed = true;
|
||||
|
||||
for (transactions_list::const_iterator x = entry->transactions.begin();
|
||||
x != entry->transactions.end();
|
||||
x++) {
|
||||
if ((*x)->amount || ((*x)->flags & TRANSACTION_VIRTUAL))
|
||||
continue;
|
||||
|
||||
if (! empty_allowed || ! balance || balance.type != value_t::AMOUNT)
|
||||
return false;
|
||||
|
||||
empty_allowed = false;
|
||||
|
||||
// If one transaction gives no value at all -- and all the
|
||||
// rest are of the same commodity -- then its value is the
|
||||
// inverse of the computed value of the others.
|
||||
|
||||
(*x)->amount = *((amount_t *) balance.data);
|
||||
(*x)->amount.negate();
|
||||
|
||||
balance = 0U;
|
||||
}
|
||||
|
||||
return ! balance;
|
||||
}
|
||||
|
||||
namespace {
|
||||
TIMER_DEF(entry_finish, "finalizing entry");
|
||||
TIMER_DEF(entry_xacts, "parsing transactions");
|
||||
|
|
@ -294,17 +207,6 @@ entry_t * parse_entry(std::istream& in, account_t * master,
|
|||
|
||||
TIMER_STOP(entry_xacts);
|
||||
|
||||
// If there were no transactions, throw away the entry
|
||||
|
||||
TIMER_START(entry_finish);
|
||||
|
||||
if (curr->transactions.empty() ||
|
||||
! parser.run_finalize_hooks(curr.get())) {
|
||||
return NULL; // ~auto_ptr will delete curr
|
||||
}
|
||||
|
||||
TIMER_STOP(entry_finish);
|
||||
|
||||
return curr.release();
|
||||
}
|
||||
|
||||
|
|
@ -423,10 +325,10 @@ unsigned int textual_parser_t::parse(std::istream& in,
|
|||
= new transaction_t(last_account, amt, TRANSACTION_VIRTUAL);
|
||||
curr->add_transaction(xact);
|
||||
|
||||
if (! run_finalize_hooks(curr.get()) ||
|
||||
! journal->add_entry(curr.release()))
|
||||
if (! journal->add_entry(curr.release()))
|
||||
throw parse_error(path, linenum,
|
||||
"Failed to record 'out' timelog entry");
|
||||
|
||||
count++;
|
||||
} else {
|
||||
throw parse_error(path, linenum, "Cannot parse timelog entry date");
|
||||
|
|
|
|||
29
textual.h
29
textual.h
|
|
@ -5,42 +5,13 @@
|
|||
|
||||
namespace ledger {
|
||||
|
||||
bool finalize_entry(entry_t * entry);
|
||||
|
||||
class textual_parser_t : public parser_t
|
||||
{
|
||||
public:
|
||||
typedef bool (*finalize_hook_t)(entry_t * entry);
|
||||
|
||||
std::list<finalize_hook_t> finalize_hooks;
|
||||
|
||||
textual_parser_t() {
|
||||
add_finalize_hook(finalize_entry);
|
||||
}
|
||||
|
||||
virtual bool test(std::istream& in) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
void add_finalize_hook(finalize_hook_t func, bool prepend = false) {
|
||||
if (prepend)
|
||||
finalize_hooks.push_front(func);
|
||||
else
|
||||
finalize_hooks.push_back(func);
|
||||
}
|
||||
void remove_finalize_hook(finalize_hook_t func) {
|
||||
finalize_hooks.remove(func);
|
||||
}
|
||||
bool run_finalize_hooks(entry_t * entry) {
|
||||
for (std::list<finalize_hook_t>::const_iterator i
|
||||
= finalize_hooks.begin();
|
||||
i != finalize_hooks.end();
|
||||
i++)
|
||||
if (! (*i)(entry))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual unsigned int parse(std::istream& in,
|
||||
journal_t * journal,
|
||||
account_t * master = NULL,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue