summaryrefslogtreecommitdiff
path: root/journal.cc
diff options
context:
space:
mode:
Diffstat (limited to 'journal.cc')
-rw-r--r--journal.cc579
1 files changed, 5 insertions, 574 deletions
diff --git a/journal.cc b/journal.cc
index ff6dbd9b..9b8d47b4 100644
--- a/journal.cc
+++ b/journal.cc
@@ -30,557 +30,12 @@
*/
#include "journal.h"
-#include "format.h"
#include "session.h"
namespace ledger {
const string version = PACKAGE_VERSION;
-bool transaction_t::use_effective_date = false;
-
-transaction_t::~transaction_t()
-{
- TRACE_DTOR(transaction_t);
-}
-
-datetime_t transaction_t::actual_date() const
-{
- if (! _date && entry)
- return entry->actual_date();
- return *_date;
-}
-
-datetime_t transaction_t::effective_date() const
-{
- if (! _date_eff && entry)
- return entry->effective_date();
- return *_date_eff;
-}
-
-bool transaction_t::valid() const
-{
- if (! entry) {
- DEBUG("ledger.validate", "transaction_t: ! entry");
- return false;
- }
-
- if (state != UNCLEARED && state != CLEARED && state != PENDING) {
- DEBUG("ledger.validate", "transaction_t: state is bad");
- return false;
- }
-
- transactions_list::const_iterator i =
- std::find(entry->transactions.begin(),
- entry->transactions.end(), this);
- if (i == entry->transactions.end()) {
- DEBUG("ledger.validate", "transaction_t: ! found");
- return false;
- }
-
- if (! account) {
- DEBUG("ledger.validate", "transaction_t: ! account");
- return false;
- }
-
- if (! amount.valid()) {
- DEBUG("ledger.validate", "transaction_t: ! amount.valid()");
- return false;
- }
-
- if (cost && ! cost->valid()) {
- DEBUG("ledger.validate", "transaction_t: cost && ! cost->valid()");
- return false;
- }
-
- if (flags() & ~0x003f) {
- DEBUG("ledger.validate", "transaction_t: flags are bad");
- return false;
- }
-
- return true;
-}
-
-void entry_base_t::add_transaction(transaction_t * xact)
-{
- transactions.push_back(xact);
-}
-
-bool entry_base_t::remove_transaction(transaction_t * xact)
-{
- transactions.remove(xact);
- return true;
-}
-
-// jww (2008-04-20): Migrate the Common Lisp version here!
-
-bool entry_base_t::finalize()
-{
- // 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.
-
- // (let ((balance 0)
- // null-xact)
-
- value_t balance;
- transaction_t * null_xact = NULL;
-
- // (do-transactions (xact entry)
- // (when (xact-must-balance-p xact)
- // (let ((amt (xact-amount* xact)))
- // (if amt
- // (setf balance (add balance (or (xact-cost xact) amt)))
- // (if null-xact
- // (error "Only one transaction with null amount allowed ~
- // per entry (beg ~S end ~S)"
- // (item-position-begin-line (entry-position entry))
- // (item-position-end-line (entry-position entry)))
- // (setf null-xact xact))))))
- //
-
- for (transactions_list::const_iterator x = transactions.begin();
- x != transactions.end();
- x++) {
- if ((*x)->must_balance()) {
- amount_t& p((*x)->cost ? *(*x)->cost : (*x)->amount);
- if (! p.is_null()) {
- if (balance.is_null())
- balance = p;
- else
- balance += p;
- } else {
- if (null_xact)
- throw_(std::logic_error,
- "Only one transaction with null amount allowed per entry");
- else
- null_xact = *x;
- }
- }
- }
- assert(balance.valid());
-
- DEBUG("ledger.journal.finalize", "initial balance = " << balance);
-
- // If there is only one transaction, balance against the default account if
- // one has been set.
-
- // (when (= 1 (length (entry-transactions entry)))
- // (if-let ((default-account
- // (journal-default-account (entry-journal entry))))
- // (setf null-xact
- // (make-transaction :entry entry
- // :status (xact-status
- // (first (entry-transactions entry)))
- // :account default-account
- // :generatedp t))
- // (add-transaction entry null-xact)))
-
- if (journal && journal->basket && transactions.size() == 1) {
- // jww (2008-07-24): Need to make the rest of the code aware of what to do
- // when it sees a generated transaction.
- null_xact = new transaction_t(journal->basket, TRANSACTION_GENERATED);
- null_xact->state = (*transactions.begin())->state;
- add_transaction(null_xact);
- }
-
- if (null_xact != NULL) {
- // If one transaction has no value at all, its value will become the
- // inverse of the rest. If multiple commodities are involved, multiple
- // transactions are generated to balance them all.
-
- // (progn
- // (if (balance-p balance)
- // (let ((first t))
- // (dolist (amount (balance-amounts balance))
- // (if first
- // (setf (xact-amount* null-xact) (negate amount)
- // first nil)
- // (add-transaction
- // entry
- // (make-transaction :entry entry
- // :account (xact-account null-xact)
- // :amount (negate amount)
- // :generatedp t)))))
- // (setf (xact-amount* null-xact) (negate balance)
- // (xact-calculatedp null-xact) t))
- //
- // (setf balance 0))
-
- if (balance.is_balance()) {
- bool first = true;
- const balance_t& bal(balance.as_balance());
- for (balance_t::amounts_map::const_iterator i = bal.amounts.begin();
- i != bal.amounts.end();
- i++) {
- if (first) {
- null_xact->amount = (*i).second.negate();
- first = false;
- } else {
- add_transaction(new transaction_t(null_xact->account,
- (*i).second.negate(),
- TRANSACTION_GENERATED));
- }
- }
- } else {
- null_xact->amount = balance.as_amount().negate();
- null_xact->add_flags(TRANSACTION_CALCULATED);
- }
- balance = NULL_VALUE;
-
- }
- else if (balance.is_balance() &&
- balance.as_balance().amounts.size() == 2) {
- // When an entry involves two different commodities (regardless of how
- // many transactions there are) determine the conversion ratio by dividing
- // the total value of one commodity by the total value of the other. This
- // establishes the per-unit cost for this transaction for both
- // commodities.
-
- // (when (and (balance-p balance)
- // (= 2 (balance-commodity-count balance)))
- // (destructuring-bind (x y) (balance-amounts balance)
- // (let ((a-commodity (amount-commodity x))
- // (per-unit-cost (value-abs (divide x y))))
- // (do-transactions (xact entry)
- // (let ((amount (xact-amount* xact)))
- // (unless (or (xact-cost xact)
- // (not (xact-must-balance-p xact))
- // (commodity-equal (amount-commodity amount)
- // a-commodity))
- // (setf balance (subtract balance amount)
- // (xact-cost xact) (multiply per-unit-cost amount)
- // balance (add balance (xact-cost xact))))))))))
-
- const balance_t& bal(balance.as_balance());
-
- balance_t::amounts_map::const_iterator a = bal.amounts.begin();
-
- const amount_t& x((*a++).second);
- const amount_t& y((*a++).second);
-
- if (! y.is_realzero()) {
- amount_t per_unit_cost = (x / y).abs();
-
- commodity_t& comm(x.commodity());
-
- for (transactions_list::const_iterator x = transactions.begin();
- x != transactions.end();
- x++) {
- const amount_t& x_amt((*x)->amount);
-
- if (! ((*x)->cost ||
- ! (*x)->must_balance() ||
- x_amt.commodity() == comm)) {
- DEBUG("ledger.journal.finalize", "before operation 1 = " << balance);
- balance -= x_amt;
- DEBUG("ledger.journal.finalize", "after operation 1 = " << balance);
- DEBUG("ledger.journal.finalize", "x_amt = " << x_amt);
- DEBUG("ledger.journal.finalize", "per_unit_cost = " << per_unit_cost);
-
- (*x)->cost = per_unit_cost * x_amt;
- DEBUG("ledger.journal.finalize", "*(*x)->cost = " << *(*x)->cost);
-
- balance += *(*x)->cost;
- DEBUG("ledger.journal.finalize", "after operation 2 = " << balance);
- }
-
- }
- }
-
- DEBUG("ledger.journal.finalize", "resolved balance = " << balance);
- }
-
- // Now that the transaction list has its final form, calculate the balance
- // once more in terms of total cost, accounting for any possible gain/loss
- // amounts.
-
- // (do-transactions (xact entry)
- // (when (xact-cost xact)
- // (let ((amount (xact-amount* xact)))
- // (assert (not (commodity-equal (amount-commodity amount)
- // (amount-commodity (xact-cost xact)))))
- // (multiple-value-bind (annotated-amount total-cost basis-cost)
- // (exchange-commodity amount :total-cost (xact-cost xact)
- // :moment (entry-date entry)
- // :tag (entry-code entry))
- // (if (annotated-commodity-p (amount-commodity amount))
- // (if-let ((price (annotation-price
- // (commodity-annotation
- // (amount-commodity amount)))))
- // (setf balance
- // (add balance (subtract basis-cost total-cost))))
- // (setf (xact-amount* xact) annotated-amount))))))
-
- for (transactions_list::const_iterator x = transactions.begin();
- x != transactions.end();
- x++) {
- if ((*x)->cost) {
- const amount_t& x_amt((*x)->amount);
-
- assert(x_amt.commodity() != (*x)->cost->commodity());
-
- entry_t * entry = dynamic_cast<entry_t *>(this);
-
- // jww (2008-07-24): Pass the entry's code here if we can, as the
- // auto-tag
- amount_t final_cost;
- amount_t basis_cost;
- amount_t ann_amount =
- commodity_t::exchange(x_amt, final_cost, basis_cost,
- (*x)->cost, none, (*x)->actual_date(),
- entry ? entry->code : optional<string>());
-
- if ((*x)->amount.commodity_annotated()) {
- if (ann_amount.annotation_details().price) {
- if (balance.is_null())
- balance = basis_cost - final_cost;
- else
- balance += basis_cost - final_cost;
- }
- } else {
- (*x)->amount = ann_amount;
- }
- }
- }
-
- DEBUG("ledger.journal.finalize", "final balance = " << balance);
-
- // (if (value-zerop balance)
- // (prog1
- // entry
- // (setf (entry-normalizedp entry) t))
- // (error "Entry does not balance (beg ~S end ~S); remaining balance is:~%~A"
- // (item-position-begin-line (entry-position entry))
- // (item-position-end-line (entry-position entry))
- // (format-value balance :width 20)))
-
- if (! balance.is_null() && ! balance.is_zero()) {
- error * err =
- new balance_error("Entry does not balance",
- new entry_context(*this, "While balancing entry:"));
- balance.round();
- err->context.push_front
- (new value_context(balance, "Unbalanced remainder is:"));
- throw err;
- }
-
- return true;
-}
-
-entry_t::entry_t(const entry_t& e)
- : entry_base_t(e), _date(e._date), _date_eff(e._date_eff),
- code(e.code), payee(e.payee)
-{
- TRACE_CTOR(entry_t, "copy");
-
- for (transactions_list::const_iterator i = transactions.begin();
- i != transactions.end();
- i++)
- (*i)->entry = this;
-}
-
-bool entry_t::get_state(transaction_t::state_t * state) const
-{
- bool first = true;
- bool hetero = false;
-
- for (transactions_list::const_iterator i = transactions.begin();
- i != transactions.end();
- i++) {
- if (first) {
- *state = (*i)->state;
- first = false;
- }
- else if (*state != (*i)->state) {
- hetero = true;
- break;
- }
- }
-
- return ! hetero;
-}
-
-void entry_t::add_transaction(transaction_t * xact)
-{
- xact->entry = this;
- entry_base_t::add_transaction(xact);
-}
-
-bool entry_t::valid() const
-{
- if (! is_valid(_date) || ! journal) {
- DEBUG("ledger.validate", "entry_t: ! _date || ! journal");
- return false;
- }
-
- for (transactions_list::const_iterator i = transactions.begin();
- i != transactions.end();
- i++)
- if ((*i)->entry != this || ! (*i)->valid()) {
- DEBUG("ledger.validate", "entry_t: transaction not valid");
- return false;
- }
-
- return true;
-}
-
-void auto_entry_t::extend_entry(entry_base_t& entry, bool post)
-{
- transactions_list initial_xacts(entry.transactions.begin(),
- entry.transactions.end());
-
- for (transactions_list::iterator i = initial_xacts.begin();
- i != initial_xacts.end();
- i++) {
- if (predicate(**i)) {
- for (transactions_list::iterator t = transactions.begin();
- t != transactions.end();
- t++) {
- amount_t amt;
- assert((*t)->amount);
- if (! (*t)->amount.commodity()) {
- if (! post)
- continue;
- assert((*i)->amount);
- amt = (*i)->amount * (*t)->amount;
- } else {
- if (post)
- continue;
- amt = (*t)->amount;
- }
-
- account_t * account = (*t)->account;
- string fullname = account->fullname();
- assert(! fullname.empty());
- if (fullname == "$account" || fullname == "@account")
- account = (*i)->account;
-
- transaction_t * xact
- = new transaction_t(account, amt, (*t)->flags() | TRANSACTION_AUTO);
-
- // Copy over details so that the resulting transaction is a mirror of
- // the automated entry's one.
- xact->state = (*t)->state;
- xact->_date = (*t)->_date;
- xact->_date_eff = (*t)->_date_eff;
- xact->note = (*t)->note;
- xact->beg_pos = (*t)->beg_pos;
- xact->beg_line = (*t)->beg_line;
- xact->end_pos = (*t)->end_pos;
- xact->end_line = (*t)->end_line;
-
- entry.add_transaction(xact);
- }
- }
- }
-}
-
-account_t::~account_t()
-{
- TRACE_DTOR(account_t);
-
- for (accounts_map::iterator i = accounts.begin();
- i != accounts.end();
- i++)
- checked_delete((*i).second);
-}
-
-account_t * account_t::find_account(const string& name,
- const bool auto_create)
-{
- accounts_map::const_iterator i = accounts.find(name);
- if (i != accounts.end())
- return (*i).second;
-
- char buf[256];
-
- string::size_type sep = name.find(':');
- assert(sep < 256|| sep == string::npos);
-
- const char * first, * rest;
- if (sep == string::npos) {
- first = name.c_str();
- rest = NULL;
- } else {
- std::strncpy(buf, name.c_str(), sep);
- buf[sep] = '\0';
-
- first = buf;
- rest = name.c_str() + sep + 1;
- }
-
- account_t * account;
-
- i = accounts.find(first);
- if (i == accounts.end()) {
- if (! auto_create)
- return NULL;
-
- account = new account_t(this, first);
- std::pair<accounts_map::iterator, bool> result
- = accounts.insert(accounts_map::value_type(first, account));
- assert(result.second);
- } else {
- account = (*i).second;
- }
-
- if (rest)
- account = account->find_account(rest, auto_create);
-
- return account;
-}
-
-string account_t::fullname() const
-{
- if (! _fullname.empty()) {
- return _fullname;
- } else {
- const account_t * first = this;
- string fullname = name;
-
- while (first->parent) {
- first = first->parent;
- if (! first->name.empty())
- fullname = first->name + ":" + fullname;
- }
-
- _fullname = fullname;
-
- return fullname;
- }
-}
-
-std::ostream& operator<<(std::ostream& out, const account_t& account)
-{
- out << account.fullname();
- return out;
-}
-
-bool account_t::valid() const
-{
- if (depth > 256) {
- DEBUG("ledger.validate", "account_t: depth > 256");
- return false;
- }
-
- for (accounts_map::const_iterator i = accounts.begin();
- i != accounts.end();
- i++) {
- if (this == (*i).second) {
- DEBUG("ledger.validate", "account_t: parent refers to itself!");
- return false;
- }
-
- if (! (*i).second->valid()) {
- DEBUG("ledger.validate", "account_t: child not valid");
- return false;
- }
- }
-
- return true;
-}
-
journal_t::journal_t(session_t * _owner) :
owner(_owner), basket(NULL), item_pool(NULL), item_pool_end(NULL)
{
@@ -592,7 +47,7 @@ journal_t::~journal_t()
{
TRACE_DTOR(journal_t);
- // Don't bother unhooking each entry's transactions from the
+ // Don't bother unhooking each entry's xacts from the
// accounts they refer to, because all accounts are about to
// be deleted.
for (entries_list::iterator i = entries.begin();
@@ -655,17 +110,17 @@ bool journal_t::add_entry(entry_t * entry)
{
entry->journal = this;
- if (! run_hooks(entry_finalize_hooks, *entry, false) ||
+ if (! entry_finalize_hooks.run_hooks(*entry, false) ||
! entry->finalize() ||
- ! run_hooks(entry_finalize_hooks, *entry, true)) {
+ ! entry_finalize_hooks.run_hooks(*entry, true)) {
entry->journal = NULL;
return false;
}
entries.push_back(entry);
- for (transactions_list::const_iterator i = entry->transactions.begin();
- i != entry->transactions.end();
+ for (xacts_list::const_iterator i = entry->xacts.begin();
+ i != entry->xacts.end();
i++)
if ((*i)->cost) {
assert((*i)->amount);
@@ -712,28 +167,4 @@ bool journal_t::valid() const
return true;
}
-void entry_context::describe(std::ostream& out) const throw()
-{
- if (! desc.empty())
- out << desc << std::endl;
-
- print_entry(out, entry, " ");
-}
-
-xact_context::xact_context(const ledger::transaction_t& _xact,
- const string& desc) throw()
- : file_context("", 0, desc), xact(_xact)
-{
- const ledger::paths_list& sources(xact.entry->journal->sources);
- unsigned int x = 0;
- for (ledger::paths_list::const_iterator i = sources.begin();
- i != sources.end();
- i++, x++)
- if (x == xact.entry->src_idx) {
- file = *i;
- break;
- }
- line = xact.beg_line;
-}
-
} // namespace ledger