summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJohn Wiegley <johnw@newartisans.com>2009-03-06 00:26:30 -0400
committerJohn Wiegley <johnw@newartisans.com>2009-03-06 00:27:29 -0400
commit2728e4d55e1c9e84ee5aae4ee8e9c380198d1c99 (patch)
tree5b5e8207db2a37684b2527acad7bccdd770fc1a4 /src
parentdd23edd5cee8e712e13a3b5eefffcc3c57bf9e10 (diff)
downloadfork-ledger-2728e4d55e1c9e84ee5aae4ee8e9c380198d1c99.tar.gz
fork-ledger-2728e4d55e1c9e84ee5aae4ee8e9c380198d1c99.tar.bz2
fork-ledger-2728e4d55e1c9e84ee5aae4ee8e9c380198d1c99.zip
Changed the way that account balances are computed
Diffstat (limited to 'src')
-rw-r--r--src/account.cc157
-rw-r--r--src/account.h79
-rw-r--r--src/filters.cc24
-rw-r--r--src/output.cc16
-rw-r--r--src/post.cc5
-rw-r--r--src/session.cc7
-rw-r--r--src/textual.cc13
-rw-r--r--src/xact.cc16
8 files changed, 234 insertions, 83 deletions
diff --git a/src/account.cc b/src/account.cc
index 56555d61..b9955120 100644
--- a/src/account.cc
+++ b/src/account.cc
@@ -30,6 +30,8 @@
*/
#include "account.h"
+#include "post.h"
+#include "xact.h"
#include "interactive.h"
namespace ledger {
@@ -166,33 +168,20 @@ namespace {
return string_value(account.name);
}
- value_t get_total(account_t& account) {
- if (! account.xdata_ || account.xdata_->family_details.total.is_null())
- return 0L;
- else
- return account.xdata_->family_details.total;
+ value_t get_amount(account_t& account) {
+ return VALUE_OR_ZERO(account.self_total());
}
- value_t get_count(account_t& account) {
- if (account.xdata_)
- return long(account.xdata_->family_details.posts_count);
- else
- return 0L;
+ value_t get_total(account_t& account) {
+ return VALUE_OR_ZERO(account.family_total());
}
value_t get_subcount(account_t& account) {
- if (account.xdata_)
- return long(account.xdata_->self_details.posts_count);
- else
- return 0L;
+ return long(account.self_details().posts_count);
}
- value_t get_amount(account_t& account) {
- if (! account.xdata_ ||
- account.xdata_->self_details.total.is_null())
- return 0L;
- else
- return account.xdata_->self_details.total;
+ value_t get_count(account_t& account) {
+ return long(account.family_details().posts_count);
}
value_t get_depth(account_t& account) {
@@ -317,4 +306,132 @@ std::size_t account_t::children_with_flags(xdata_t::flags_t flags) const
return count;
}
+account_t::xdata_t::details_t&
+account_t::xdata_t::details_t::operator+=(const details_t& other)
+{
+ // jww (2009-03-05): NYI
+ return *this;
+}
+
+value_t account_t::self_total(const optional<expr_t&>& expr) const
+{
+ if (xdata_ && xdata_->has_flags(ACCOUNT_EXT_VISITED)) {
+ if (! xdata_) xdata_ = xdata_t();
+
+ posts_deque::const_iterator i =
+ posts.begin() + xdata_->self_details.last_size;
+
+ for (; i != posts.end(); i++) {
+ if ((*i)->xdata().has_flags(POST_EXT_VISITED) &&
+ ! (*i)->xdata().has_flags(POST_EXT_CONSIDERED)) {
+ (*i)->add_to_value(xdata_->self_details.total, expr);
+ (*i)->xdata().add_flags(POST_EXT_CONSIDERED);
+ }
+ }
+
+ xdata_->self_details.last_size = posts.size();
+
+ return xdata_->self_details.total;
+ } else {
+ return NULL_VALUE;
+ }
+}
+
+value_t account_t::family_total(const optional<expr_t&>& expr) const
+{
+ if (! (xdata_ && xdata_->family_details.calculated)) {
+ const_cast<account_t&>(*this).xdata().family_details.calculated = true;
+
+ value_t temp;
+ foreach (const accounts_map::value_type& pair, accounts) {
+ temp = pair.second->family_total(expr);
+ if (! temp.is_null())
+ add_or_set_value(xdata_->family_details.total, temp);
+ }
+
+ temp = self_total(expr);
+ if (! temp.is_null())
+ add_or_set_value(xdata_->family_details.total, temp);
+ }
+ return xdata_->family_details.total;
+}
+
+const account_t::xdata_t::details_t&
+account_t::self_details(bool gather_all) const
+{
+ if (! (xdata_ && xdata_->self_details.gathered)) {
+ const_cast<account_t&>(*this).xdata().self_details.gathered = true;
+
+ foreach (const post_t * post, posts)
+ xdata_->self_details.update(const_cast<post_t&>(*post), gather_all);
+ }
+ return xdata_->self_details;
+}
+
+const account_t::xdata_t::details_t&
+account_t::family_details(bool gather_all) const
+{
+ if (! (xdata_ && xdata_->family_details.gathered)) {
+ const_cast<account_t&>(*this).xdata().family_details.gathered = true;
+
+ foreach (const accounts_map::value_type& pair, accounts)
+ xdata_->family_details += pair.second->family_details(gather_all);
+
+ xdata_->self_details += self_details(gather_all);
+ }
+ return xdata_->family_details;
+}
+
+void account_t::xdata_t::details_t::update(post_t& post,
+ bool gather_all)
+{
+ if (last_xact != post.xact) {
+ xacts_count++;
+ last_xact = post.xact;
+ }
+ if (last_post == &post)
+ return;
+
+ last_post = &post;
+
+ posts_count++;
+ if (post.has_flags(POST_VIRTUAL))
+ posts_virtuals_count++;
+
+ if (gather_all)
+ filenames.insert(post.pathname);
+
+ date_t date = post.date();
+
+ if (date.year() == CURRENT_DATE().year() &&
+ date.month() == CURRENT_DATE().month())
+ posts_this_month_count++;
+
+ if ((CURRENT_DATE() - date).days() <= 30)
+ posts_last_30_count++;
+ if ((CURRENT_DATE() - date).days() <= 7)
+ posts_last_7_count++;
+
+ if (! is_valid(earliest_post) || post.date() < earliest_post)
+ earliest_post = post.date();
+ if (! is_valid(latest_post) || post.date() > latest_post)
+ latest_post = post.date();
+
+ if (post.state() == item_t::CLEARED) {
+ posts_cleared_count++;
+
+ if (! is_valid(earliest_cleared_post) ||
+ post.date() < earliest_cleared_post)
+ earliest_cleared_post = post.date();
+ if (! is_valid(latest_cleared_post) ||
+ post.date() > latest_cleared_post)
+ latest_cleared_post = post.date();
+ }
+
+ if (gather_all) {
+ accounts_referenced.insert(post.account->fullname());
+ payees_referenced.insert(post.xact->payee);
+ }
+}
+
} // namespace ledger
diff --git a/src/account.h b/src/account.h
index dd7ac3d6..f161a11f 100644
--- a/src/account.h
+++ b/src/account.h
@@ -50,9 +50,12 @@
namespace ledger {
-class account_t;
class session_t;
+class account_t;
+class xact_t;
+class post_t;
+typedef std::deque<post_t *> posts_deque;
typedef std::map<const string, account_t *> accounts_map;
/**
@@ -68,14 +71,15 @@ class account_t : public scope_t
optional<string> note;
unsigned short depth;
accounts_map accounts;
+ posts_deque posts;
bool known;
mutable void * data;
mutable string _fullname;
- account_t(account_t * _parent = NULL,
- const string& _name = "",
- const optional<string>& _note = none)
+ account_t(account_t * _parent = NULL,
+ const string& _name = "",
+ const optional<string>& _note = none)
: scope_t(), parent(_parent), name(_name), note(_note),
depth(static_cast<unsigned short>(parent ? parent->depth + 1 : 0)),
known(false), data(NULL) {
@@ -112,6 +116,10 @@ class account_t : public scope_t
account_t * find_account(const string& name, bool auto_create = true);
account_t * find_account_re(const string& regexp);
+ void add_post(post_t * post) {
+ posts.push_back(post);
+ }
+
virtual expr_t::ptr_op_t lookup(const string& name);
bool valid() const;
@@ -130,14 +138,61 @@ class account_t : public scope_t
struct details_t
{
- value_t total;
+ value_t total;
+ bool calculated;
+ bool gathered;
+
+ // The following are only calculated if --totals is enabled
+ std::size_t xacts_count;
+
+ std::size_t posts_count;
+ std::size_t posts_virtuals_count;
+ std::size_t posts_cleared_count;
+ std::size_t posts_last_7_count;
+ std::size_t posts_last_30_count;
+ std::size_t posts_this_month_count;
+
+ date_t earliest_post;
+ date_t earliest_cleared_post;
+ date_t latest_post;
+ date_t latest_cleared_post;
+
+ xact_t * last_xact;
+ post_t * last_post;
+
+ std::size_t last_size;
- std::size_t posts_count;
- std::size_t posts_virtuals_count;
+ // The following are only calculated if --gather is enabled
+ std::set<path> filenames;
+ std::set<string> accounts_referenced;
+ std::set<string> payees_referenced;
details_t()
- : posts_count(0),
- posts_virtuals_count(0) {}
+ : calculated(false),
+ gathered(false),
+
+ xacts_count(0),
+
+ posts_count(0),
+ posts_virtuals_count(0),
+ posts_cleared_count(0),
+ posts_last_7_count(0),
+ posts_last_30_count(0),
+ posts_this_month_count(0),
+
+ earliest_post(0),
+ earliest_cleared_post(0),
+ latest_post(0),
+ latest_cleared_post(0),
+
+ last_xact(NULL),
+ last_post(NULL),
+
+ last_size(0) {}
+
+ details_t& operator+=(const details_t& other);
+
+ void update(post_t& post, bool gather_all = false);
};
details_t self_details;
@@ -185,6 +240,12 @@ class account_t : public scope_t
return *xdata_;
}
+ value_t self_total(const optional<expr_t&>& expr = none) const;
+ value_t family_total(const optional<expr_t&>& expr = none) const;
+
+ const xdata_t::details_t& self_details(bool gather_all = true) const;
+ const xdata_t::details_t& family_details(bool gather_all = true) const;
+
bool has_flags(xdata_t::flags_t flags) const {
return xdata_ && xdata_->has_flags(flags);
}
diff --git a/src/filters.cc b/src/filters.cc
index 2895bf7f..f8736667 100644
--- a/src/filters.cc
+++ b/src/filters.cc
@@ -207,30 +207,8 @@ void calc_posts::operator()(post_t& post)
account_t * acct = post.reported_account();
acct->xdata().add_flags(ACCOUNT_EXT_VISITED);
- if (! account_wise) {
+ if (! account_wise)
add_or_set_value(xdata.total, xdata.visited_value);
- } else {
- account_t::xdata_t * acct_xdata = &acct->xdata();
-
- add_or_set_value(acct_xdata->self_details.total, xdata.visited_value);
-
- acct_xdata->self_details.posts_count++;
- acct_xdata->self_details.posts_virtuals_count++;
-
- while (true) {
- add_or_set_value(acct_xdata->family_details.total, xdata.visited_value);
-
- acct_xdata->family_details.posts_count++;
- if (post.has_flags(POST_VIRTUAL))
- acct_xdata->family_details.posts_virtuals_count++;
-
- acct = acct->parent;
- if (acct)
- acct_xdata = &acct->xdata();
- else
- break;
- }
- }
item_handler<post_t>::operator()(post);
diff --git a/src/output.cc b/src/output.cc
index f10de042..975d8077 100644
--- a/src/output.cc
+++ b/src/output.cc
@@ -297,17 +297,11 @@ void format_accounts::flush()
}
}
- if (report.session.master->has_xdata()) {
- account_t::xdata_t& xdata(report.session.master->xdata());
-
- if (! report.HANDLED(no_total) && top_displayed > 1 &&
- xdata.family_details.total) {
- xdata.self_details.total = xdata.family_details.total;
-
- bind_scope_t bound_scope(report, *report.session.master);
- separator_format.format(out, bound_scope);
- total_line_format.format(out, bound_scope);
- }
+ if (! report.HANDLED(no_total) && top_displayed > 1 &&
+ report.session.master->family_total()) {
+ bind_scope_t bound_scope(report, *report.session.master);
+ separator_format.format(out, bound_scope);
+ total_line_format.format(out, bound_scope);
}
out.flush();
diff --git a/src/post.cc b/src/post.cc
index 42e81159..2c303a87 100644
--- a/src/post.cc
+++ b/src/post.cc
@@ -231,10 +231,11 @@ namespace {
DEBUG("post.account_amount", "Found account: " << account->fullname());
- if (account->xdata().self_details.total.is_null())
+ value_t total = account->self_total();
+ if (total.is_null())
return 0L;
else
- return account->xdata().self_details.total.simplified();
+ return total.simplified();
}
value_t get_account_depth(post_t& post) {
diff --git a/src/session.cc b/src/session.cc
index ff23719b..e171ff4d 100644
--- a/src/session.cc
+++ b/src/session.cc
@@ -87,9 +87,14 @@ std::size_t session_t::read_journal(std::istream& in,
{
if (! master)
master = journal->master;
+
std::size_t count = journal->parse(in, *this, master, &pathname,
HANDLED(strict));
- clean_accounts(); // remove calculated totals
+
+ // remove calculated totals and flags
+ clean_posts();
+ clean_accounts();
+
return count;
}
diff --git a/src/textual.cc b/src/textual.cc
index 1c3bcb02..3652176a 100644
--- a/src/textual.cc
+++ b/src/textual.cc
@@ -1013,25 +1013,24 @@ post_t * instance_t::parse_post(char * line,
DEBUG("textual.parse", "line " << linenum << ": "
<< "POST assign: parsed amt = " << *post->assigned_amount);
- account_t::xdata_t& xdata(post->account->xdata());
- amount_t& amt(*post->assigned_amount);
+ amount_t& amt(*post->assigned_amount);
+ value_t account_total(post->account->self_total(false));
DEBUG("post.assign", "line " << linenum << ": "
- "account balance = " << xdata.self_details.total);
+ "account balance = " << account_total);
DEBUG("post.assign", "line " << linenum << ": "
"post amount = " << amt);
amount_t diff;
- switch (xdata.self_details.total.type()) {
+ switch (account_total.type()) {
case value_t::AMOUNT:
- diff = amt - xdata.self_details.total.as_amount();
+ diff = amt - account_total.as_amount();
break;
case value_t::BALANCE:
if (optional<amount_t> comm_bal =
- xdata.self_details.total.as_balance()
- .commodity_amount(amt.commodity()))
+ account_total.as_balance().commodity_amount(amt.commodity()))
diff = amt - *comm_bal;
else
diff = amt;
diff --git a/src/xact.cc b/src/xact.cc
index 8fb1d14c..0ad223a1 100644
--- a/src/xact.cc
+++ b/src/xact.cc
@@ -306,7 +306,7 @@ bool xact_base_t::finalize()
throw_(balance_error, _("Transaction does not balance"));
}
- // Add the final calculated totals each to their related account
+ // Add a pointer to each posting to their related accounts
if (dynamic_cast<xact_t *>(this)) {
bool all_null = true;
@@ -314,19 +314,15 @@ bool xact_base_t::finalize()
foreach (post_t * post, posts) {
if (! post->amount.is_null()) {
all_null = false;
-
post->amount.in_place_reduce();
-
- add_or_set_value(post->account->xdata().self_details.total,
- post->amount);
-
- DEBUG("xact.finalize.totals",
- "Total for " << post->account->fullname() << " + "
- << post->amount << ": "
- << post->account->xdata().self_details.total);
} else {
some_null = true;
}
+
+ post->account->add_post(post);
+
+ post->xdata().add_flags(POST_EXT_VISITED);
+ post->account->xdata().add_flags(ACCOUNT_EXT_VISITED);
}
if (all_null)
return false; // ignore this xact completely