summaryrefslogtreecommitdiff
path: root/src/filters.cc
diff options
context:
space:
mode:
authorJohn Wiegley <johnw@newartisans.com>2012-02-28 02:34:37 -0600
committerJohn Wiegley <johnw@newartisans.com>2012-02-28 02:34:37 -0600
commit6adfcc8469e3d526f4bcb0971b49efb490ad6401 (patch)
tree4185b2ebbd668b7ded2808a9cd8a5edccf39d8bc /src/filters.cc
parent887f429ae40934c145e03b03cc452e6af4457c0f (diff)
downloadfork-ledger-6adfcc8469e3d526f4bcb0971b49efb490ad6401.tar.gz
fork-ledger-6adfcc8469e3d526f4bcb0971b49efb490ad6401.tar.bz2
fork-ledger-6adfcc8469e3d526f4bcb0971b49efb490ad6401.zip
Rewrite the way interval reports are generated
Diffstat (limited to 'src/filters.cc')
-rw-r--r--src/filters.cc129
1 files changed, 82 insertions, 47 deletions
diff --git a/src/filters.cc b/src/filters.cc
index fbef1cd8..fa1f6fa2 100644
--- a/src/filters.cc
+++ b/src/filters.cc
@@ -904,69 +904,104 @@ void subtotal_posts::operator()(post_t& post)
void interval_posts::report_subtotal(const date_interval_t& ival)
{
- if (last_post && ival) {
- if (exact_periods)
- subtotal_posts::report_subtotal();
- else
- subtotal_posts::report_subtotal(NULL, ival);
- }
+ if (exact_periods)
+ subtotal_posts::report_subtotal();
+ else
+ subtotal_posts::report_subtotal(NULL, ival);
+}
- last_post = NULL;
+namespace {
+ struct sort_posts_by_date {
+ bool operator()(post_t * left, post_t * right) const {
+ return left->date() < right->date();
+ }
+ };
}
void interval_posts::operator()(post_t& post)
{
- DEBUG("filters.interval", "Considering post with amount " << post.amount);
-#if defined(DEBUG_ON)
- DEBUG("filters.interval", "interval is:");
- debug_interval(interval);
-#endif
- if (! interval.find_period(post.date())) {
- DEBUG("filters.interval", "Post does not fall within period");
+ // If there is a duration (such as weekly), we must generate the
+ // report in two passes. Otherwise, we only have to check whether the
+ // post falls within the reporting period.
+
+ if (interval.duration) {
+ all_posts.push_back(&post);
+ }
+ else if (interval.find_period(post.date()))
+ item_handler<post_t>::operator()(post);
+}
+
+void interval_posts::flush()
+{
+ if (! interval.duration) {
+ item_handler<post_t>::flush();
return;
}
- if (interval.duration) {
- DEBUG("filters.interval", "There is an interval duration");
- if (interval != last_interval) {
-#if defined(DEBUG_ON)
- DEBUG("filters.interval", "interval != last_interval, so reporting");
- DEBUG("filters.interval", "last_interval is:");
- debug_interval(last_interval);
-#endif
- report_subtotal(last_interval);
+ // Sort all the postings we saw by date ascending
+ std::stable_sort(all_posts.begin(), all_posts.end(),
+ sort_posts_by_date());
- if (generate_empty_posts) {
- for (++last_interval; last_interval < interval; ++last_interval) {
- // Generate a null posting, so the intervening periods can be
- // seen when -E is used, or if the calculated amount ends up being
- // non-zero
- xact_t& null_xact = temps.create_xact();
- null_xact._date = last_interval.inclusive_end();
+ // Determine the beginning interval by using the earliest post
+ if (! interval.find_period(all_posts.front()->date()))
+ throw_(std::logic_error, _("Failed to find period for interval report"));
- post_t& null_post = temps.create_post(null_xact, empty_account);
- null_post.add_flags(POST_CALCULATED);
- null_post.amount = 0L;
+ // Walk the interval forward reporting all posts within each one
+ // before moving on, until we reach the end of all_posts
+ bool saw_posts = false;
+ for (std::deque<post_t *>::iterator i = all_posts.begin();
+ i != all_posts.end(); ) {
+ post_t * post(*i);
- last_post = &null_post;
- subtotal_posts::operator()(null_post);
+ DEBUG("filters.interval",
+ "Considering post " << post->date() << " = " << post->amount);
+#if defined(DEBUG_ON)
+ DEBUG("filters.interval", "interval is:");
+ debug_interval(interval);
+#endif
+ assert(! interval.finish || post->date() < *interval.finish);
- report_subtotal(last_interval);
- }
- assert(last_interval <= interval);
- } else {
- DEBUG("filters.interval", "Setting last_interval = interval");
- last_interval = interval;
+ if (interval.within_period(post->date())) {
+ DEBUG("filters.interval", "Calling subtotal_posts::operator()");
+ subtotal_posts::operator()(*post);
+ ++i;
+ saw_posts = true;
+ } else {
+ if (saw_posts) {
+ DEBUG("filters.interval",
+ "Calling subtotal_posts::report_subtotal()");
+ report_subtotal(interval);
+ saw_posts = false;
}
+ else if (generate_empty_posts) {
+ // Generate a null posting, so the intervening periods can be
+ // seen when -E is used, or if the calculated amount ends up
+ // being non-zero
+ xact_t& null_xact = temps.create_xact();
+ null_xact._date = interval.inclusive_end();
+
+ post_t& null_post = temps.create_post(null_xact, empty_account);
+ null_post.add_flags(POST_CALCULATED);
+ null_post.amount = 0L;
+
+ subtotal_posts::operator()(null_post);
+ report_subtotal(interval);
+ }
+
+ DEBUG("filters.interval", "Advancing interval");
+ ++interval;
}
- DEBUG("filters.interval", "Calling subtotal_posts::operator()");
- subtotal_posts::operator()(post);
- } else {
- DEBUG("filters.interval", "There is no interval duration");
- item_handler<post_t>::operator()(post);
}
- last_post = &post;
+ // If the last postings weren't reported, do so now.
+ if (saw_posts) {
+ DEBUG("filters.interval",
+ "Calling subtotal_posts::report_subtotal() at end");
+ report_subtotal(interval);
+ }
+
+ // Tell our parent class to flush
+ subtotal_posts::flush();
}
void posts_as_equity::report_subtotal()