From 7bddcd676bc53c6caad7dd283be362fcd53d5721 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 23 May 2010 15:24:02 -0600 Subject: Added --rounding option, which is off by default The purpose of this option is to add special "" postings, to ensure that a regiter's running total is *always* the sum of its postings. Within --rounding, these adjustment postings are missing, which was the behavior in Ledger 2.x. It can be orders of magnitude slower to turn it on for large reports with many commodities. --- src/filters.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/filters.h') diff --git a/src/filters.h b/src/filters.h index 82fbf687..ced51f3f 100644 --- a/src/filters.h +++ b/src/filters.h @@ -381,6 +381,7 @@ class changed_value_posts : public item_handler bool changed_values_only; bool for_accounts_report; bool show_unrealized; + bool show_rounding; post_t * last_post; value_t last_total; value_t last_display_total; @@ -396,7 +397,8 @@ public: changed_value_posts(post_handler_ptr handler, report_t& _report, bool _for_accounts_report, - bool _show_unrealized); + bool _show_unrealized, + bool _show_rounding); virtual ~changed_value_posts() { TRACE_DTOR(changed_value_posts); -- cgit v1.2.3 From 8f17d01f5e48ae5097f4cb38d481b00577329b8c Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 30 May 2010 02:20:34 -0600 Subject: Added new required item_handler_t::clear() method --- src/chain.h | 5 ++ src/filters.h | 168 +++++++++++++++++++++++++++++++++++++++++++++++++++++----- src/output.h | 29 ++++++++++ src/temps.cc | 45 +++++++++------- src/temps.h | 6 ++- src/xml.h | 8 +++ 6 files changed, 227 insertions(+), 34 deletions(-) (limited to 'src/filters.h') diff --git a/src/chain.h b/src/chain.h index 94d54317..fbde9f0a 100644 --- a/src/chain.h +++ b/src/chain.h @@ -75,6 +75,11 @@ public: (*handler.get())(item); } } + + virtual void clear() { + if (handler) + handler->clear(); + } }; typedef shared_ptr > post_handler_ptr; diff --git a/src/filters.h b/src/filters.h index ced51f3f..3f3e3d34 100644 --- a/src/filters.h +++ b/src/filters.h @@ -88,6 +88,11 @@ public: virtual void operator()(post_t& post) { posts.push_back(&post); } + + virtual void clear() { + posts.clear(); + item_handler::clear(); + } }; class posts_iterator; @@ -149,27 +154,34 @@ public: virtual void flush(); virtual void operator()(post_t& post); + + virtual void clear() { + completed = false; + posts.clear(); + xacts_seen = 0; + last_xact = NULL; + + item_handler::clear(); + } }; class sort_posts : public item_handler { typedef std::deque posts_deque; - posts_deque posts; - const expr_t sort_order; + posts_deque posts; + expr_t sort_order; sort_posts(); public: - sort_posts(post_handler_ptr handler, - const expr_t& _sort_order) + sort_posts(post_handler_ptr handler, const expr_t& _sort_order) : item_handler(handler), sort_order(_sort_order) { TRACE_CTOR(sort_posts, "post_handler_ptr, const value_expr&"); } - sort_posts(post_handler_ptr handler, - const string& _sort_order) + sort_posts(post_handler_ptr handler, const string& _sort_order) : item_handler(handler), sort_order(_sort_order) { TRACE_CTOR(sort_posts, @@ -189,6 +201,13 @@ public: virtual void operator()(post_t& post) { posts.push_back(&post); } + + virtual void clear() { + posts.clear(); + sort_order.mark_uncompiled(); + + item_handler::clear(); + } }; class sort_xacts : public item_handler @@ -199,14 +218,12 @@ class sort_xacts : public item_handler sort_xacts(); public: - sort_xacts(post_handler_ptr handler, - const expr_t& _sort_order) + sort_xacts(post_handler_ptr handler, const expr_t& _sort_order) : sorter(handler, _sort_order) { TRACE_CTOR(sort_xacts, "post_handler_ptr, const value_expr&"); } - sort_xacts(post_handler_ptr handler, - const string& _sort_order) + sort_xacts(post_handler_ptr handler, const string& _sort_order) : sorter(handler, _sort_order) { TRACE_CTOR(sort_xacts, "post_handler_ptr, const string&"); @@ -228,6 +245,13 @@ public: last_xact = post.xact; } + + virtual void clear() { + sorter.clear(); + last_xact = NULL; + + item_handler::clear(); + } }; class filter_posts : public item_handler @@ -255,6 +279,11 @@ public: (*handler)(post); } } + + virtual void clear() { + pred.mark_uncompiled(); + item_handler::clear(); + } }; class anonymize_posts : public item_handler @@ -274,6 +303,13 @@ public: } virtual void operator()(post_t& post); + + virtual void clear() { + temps.clear(); + last_xact = NULL; + + item_handler::clear(); + } }; class calc_posts : public item_handler @@ -297,6 +333,13 @@ public: } virtual void operator()(post_t& post); + + virtual void clear() { + last_post = NULL; + amount_expr.mark_uncompiled(); + + item_handler::clear(); + } }; class collapse_posts : public item_handler @@ -334,13 +377,29 @@ public: } virtual void flush() { - report_subtotal(); + report_subtotal(); item_handler::flush(); } void report_subtotal(); virtual void operator()(post_t& post); + + virtual void clear() { + amount_expr.mark_uncompiled(); + display_predicate.mark_uncompiled(); + only_predicate.mark_uncompiled(); + + subtotal = value_t(); + count = 0; + last_xact = NULL; + last_post = NULL; + + temps.clear(); + component_posts.clear(); + + item_handler::clear(); + } }; class related_posts : public item_handler @@ -367,6 +426,11 @@ public: post.xdata().add_flags(POST_EXT_RECEIVED); posts.push_back(&post); } + + virtual void clear() { + posts.clear(); + item_handler::clear(); + } }; class changed_value_posts : public item_handler @@ -410,6 +474,20 @@ public: void output_rounding(post_t& post); virtual void operator()(post_t& post); + + virtual void clear() { + display_amount_expr.mark_uncompiled(); + total_expr.mark_uncompiled(); + display_total_expr.mark_uncompiled(); + + last_post = NULL; + last_total = value_t(); + last_display_total = value_t(); + + temps.clear(); + + item_handler::clear(); + } }; class subtotal_posts : public item_handler @@ -471,10 +549,20 @@ public: item_handler::flush(); } virtual void operator()(post_t& post); + + virtual void clear() { + amount_expr.mark_uncompiled(); + values.clear(); + temps.clear(); + component_posts.clear(); + + item_handler::clear(); + } }; class interval_posts : public subtotal_posts { + date_interval_t start_interval; date_interval_t interval; date_interval_t last_interval; post_t * last_post; @@ -491,8 +579,9 @@ public: const date_interval_t& _interval, bool _exact_periods = false, bool _generate_empty_posts = false) - : subtotal_posts(_handler, amount_expr), interval(_interval), - last_post(NULL), empty_account(temps.create_account(_(""))), + : subtotal_posts(_handler, amount_expr), start_interval(_interval), + interval(start_interval), last_post(NULL), + empty_account(temps.create_account(_(""))), exact_periods(_exact_periods), generate_empty_posts(_generate_empty_posts) { TRACE_CTOR(interval_posts, @@ -512,6 +601,14 @@ public: } } virtual void operator()(post_t& post); + + virtual void clear() { + interval = start_interval; + last_interval = date_interval_t(); + last_post = NULL; + + item_handler::clear(); + } }; class posts_as_equity : public subtotal_posts @@ -539,6 +636,11 @@ public: report_subtotal(); subtotal_posts::flush(); } + + virtual void clear() { + last_post = NULL; + item_handler::clear(); + } }; class by_payee_posts : public item_handler @@ -562,6 +664,13 @@ class by_payee_posts : public item_handler virtual void flush(); virtual void operator()(post_t& post); + + virtual void clear() { + amount_expr.mark_uncompiled(); + payee_subtotals.clear(); + + item_handler::clear(); + } }; class transfer_details : public item_handler @@ -595,6 +704,13 @@ public: } virtual void operator()(post_t& post); + + virtual void clear() { + expr.mark_uncompiled(); + temps.clear(); + + item_handler::clear(); + } }; class dow_posts : public subtotal_posts @@ -616,6 +732,13 @@ public: virtual void operator()(post_t& post) { days_of_the_week[post.date().day_of_week()].push_back(&post); } + + virtual void clear() { + for (int i = 0; i < 7; i++) + days_of_the_week[i].clear(); + + item_handler::clear(); + } }; class generate_posts : public item_handler @@ -642,6 +765,13 @@ public: void add_period_xacts(period_xacts_list& period_xacts); virtual void add_post(const date_interval_t& period, post_t& post); + + virtual void clear() { + pending_posts.clear(); + temps.clear(); + + item_handler::clear(); + } }; class budget_posts : public generate_posts @@ -692,6 +822,11 @@ class forecast_posts : public generate_posts virtual void add_post(const date_interval_t& period, post_t& post); virtual void flush(); + + virtual void clear() { + pred.mark_uncompiled(); + item_handler::clear(); + } }; ////////////////////////////////////////////////////////////////////// @@ -717,6 +852,13 @@ public: virtual ~pass_down_accounts() { TRACE_DTOR(pass_down_accounts); } + + virtual void clear() { + if (pred) + pred->mark_uncompiled(); + + item_handler::clear(); + } }; } // namespace ledger diff --git a/src/output.h b/src/output.h index 00c664c1..f0e7f9a5 100644 --- a/src/output.h +++ b/src/output.h @@ -75,6 +75,13 @@ public: virtual void flush(); virtual void operator()(post_t& post); + + virtual void clear() { + last_xact = NULL; + last_post = NULL; + + item_handler::clear(); + } }; class format_accounts : public item_handler @@ -105,6 +112,13 @@ public: virtual void flush(); virtual void operator()(account_t& account); + + virtual void clear() { + disp_pred.mark_uncompiled(); + posted_accounts.clear(); + + item_handler::clear(); + } }; class report_accounts : public item_handler @@ -126,6 +140,11 @@ public: virtual void flush(); virtual void operator()(post_t& post); + + virtual void clear() { + accounts.clear(); + item_handler::clear(); + } }; class report_payees : public item_handler @@ -147,6 +166,11 @@ public: virtual void flush(); virtual void operator()(post_t& post); + + virtual void clear() { + payees.clear(); + item_handler::clear(); + } }; class report_commodities : public item_handler @@ -168,6 +192,11 @@ public: virtual void flush(); virtual void operator()(post_t& post); + + virtual void clear() { + commodities.clear(); + item_handler::clear(); + } }; } // namespace ledger diff --git a/src/temps.cc b/src/temps.cc index dcaa9101..7a630176 100644 --- a/src/temps.cc +++ b/src/temps.cc @@ -38,26 +38,6 @@ namespace ledger { -temporaries_t::~temporaries_t() -{ - if (post_temps) { - foreach (post_t& post, *post_temps) { - if (! post.xact->has_flags(ITEM_TEMP)) - post.xact->remove_post(&post); - - if (post.account && ! post.account->has_flags(ACCOUNT_TEMP)) - post.account->remove_post(&post); - } - } - - if (acct_temps) { - foreach (account_t& acct, *acct_temps) { - if (acct.parent && ! acct.parent->has_flags(ACCOUNT_TEMP)) - acct.parent->remove_account(&acct); - } - } -} - xact_t& temporaries_t::copy_xact(xact_t& origin) { if (! xact_temps) @@ -134,4 +114,29 @@ account_t& temporaries_t::create_account(const string& name, return temp; } +void temporaries_t::clear() +{ + if (post_temps) { + foreach (post_t& post, *post_temps) { + if (! post.xact->has_flags(ITEM_TEMP)) + post.xact->remove_post(&post); + + if (post.account && ! post.account->has_flags(ACCOUNT_TEMP)) + post.account->remove_post(&post); + } + post_temps->clear(); + } + + if (xact_temps) + xact_temps->clear(); + + if (acct_temps) { + foreach (account_t& acct, *acct_temps) { + if (acct.parent && ! acct.parent->has_flags(ACCOUNT_TEMP)) + acct.parent->remove_account(&acct); + } + acct_temps->clear(); + } +} + } // namespace ledger diff --git a/src/temps.h b/src/temps.h index ac6d08cd..210bbf63 100644 --- a/src/temps.h +++ b/src/temps.h @@ -51,7 +51,9 @@ class temporaries_t optional > acct_temps; public: - ~temporaries_t(); + ~temporaries_t() { + clear(); + } xact_t& copy_xact(xact_t& origin); xact_t& create_xact(); @@ -69,6 +71,8 @@ public: account_t& last_account() { return acct_temps->back(); } + + void clear(); }; } // namespace ledger diff --git a/src/xml.h b/src/xml.h index 320096f8..5d14dab3 100644 --- a/src/xml.h +++ b/src/xml.h @@ -83,6 +83,14 @@ public: virtual void flush(); virtual void operator()(post_t& post); + + virtual void clear() { + commodities.clear(); + transactions_set.clear(); + transactions.clear(); + + item_handler::clear(); + } }; } // namespace ledger -- cgit v1.2.3 From e86a4767bc93664893faa4d0f03619f57302c7d1 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 30 May 2010 02:27:46 -0600 Subject: Added new post_splitter posting handler --- src/filters.cc | 43 ++++++++++++++++++++++++++++++++++++++++++- src/filters.h | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 1 deletion(-) (limited to 'src/filters.h') diff --git a/src/filters.cc b/src/filters.cc index 57c95cd3..6915144d 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -39,8 +39,49 @@ namespace ledger { +void post_splitter::print_title(const value_t& val) +{ + if (! report.HANDLED(no_titles)) { + std::ostringstream buf; + val.print(buf); + post_chain->title(buf.str()); + } +} + +void post_splitter::flush() +{ + foreach (value_to_posts_map::value_type pair, posts_map) { + preflush_func(pair.first); + + foreach (post_t * post, pair.second) + (*post_chain)(*post); + + post_chain->flush(); + post_chain->clear(); + + if (postflush_func) + (*postflush_func)(pair.first); + } +} + +void post_splitter::operator()(post_t& post) +{ + bind_scope_t bound_scope(report, post); + value_t result(group_by_expr.calc(bound_scope)); + + value_to_posts_map::iterator i = posts_map.find(result); + if (i != posts_map.end()) { + (*i).second.push_back(&post); + } else { + std::pair inserted + = posts_map.insert(value_to_posts_map::value_type(result, posts_list())); + assert(inserted.second); + (*inserted.first).second.push_back(&post); + } +} + pass_down_posts::pass_down_posts(post_handler_ptr handler, - posts_iterator& iter) + posts_iterator& iter) : item_handler(handler) { TRACE_CTOR(pass_down_posts, "post_handler_ptr, posts_iterator"); diff --git a/src/filters.h b/src/filters.h index 3f3e3d34..dd6b3b1a 100644 --- a/src/filters.h +++ b/src/filters.h @@ -50,6 +50,56 @@ namespace ledger { +////////////////////////////////////////////////////////////////////// +// +// Posting collector +// + +class post_splitter : public item_handler +{ +public: + typedef std::map value_to_posts_map; + typedef function custom_flusher_t; + +protected: + value_to_posts_map posts_map; + report_t& report; + post_handler_ptr post_chain; + expr_t group_by_expr; + custom_flusher_t preflush_func; + optional postflush_func; + +public: + post_splitter(report_t& _report, + post_handler_ptr _post_chain, + expr_t _group_by_expr) + : report(_report), post_chain(_post_chain), + group_by_expr(_group_by_expr), + preflush_func(bind(&post_splitter::print_title, this, _1)) { + TRACE_CTOR(post_splitter, "scope_t&, post_handler_ptr, expr_t"); + } + virtual ~post_splitter() { + TRACE_DTOR(post_splitter); + } + + void set_preflush_func(custom_flusher_t functor) { + preflush_func = functor; + } + void set_postflush_func(custom_flusher_t functor) { + postflush_func = functor; + } + + virtual void print_title(const value_t& val); + + virtual void flush(); + virtual void operator()(post_t& post); + + virtual void clear() { + posts_map.clear(); + post_chain->clear(); + } +}; + ////////////////////////////////////////////////////////////////////// // // Posting filters -- cgit v1.2.3 From 5a2644c1b7220dc96e4de9cfb82f6d829cf34321 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 30 May 2010 20:49:50 -0600 Subject: -V/-X options now take price history into account --- src/filters.cc | 80 ++++++++++++++++++++++++++++++++++++++++++-- src/filters.h | 2 ++ test/baseline/opt-price.test | 3 +- test/regress/D943AE0F.test | 4 +-- 4 files changed, 84 insertions(+), 5 deletions(-) (limited to 'src/filters.h') diff --git a/src/filters.cc b/src/filters.cc index eabf9834..bca8516e 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -503,6 +503,8 @@ changed_value_posts::changed_value_posts(post_handler_ptr handler, void changed_value_posts::flush() { if (last_post && last_post->date() <= report.terminus.date()) { + if (! for_accounts_report) + output_intermediate_prices(*last_post, report.terminus.date()); output_revaluation(*last_post, report.terminus.date()); last_post = NULL; } @@ -514,7 +516,6 @@ void changed_value_posts::output_revaluation(post_t& post, const date_t& date) if (is_valid(date)) post.xdata().date = date; - value_t repriced_total; try { bind_scope_t bound_scope(report, post); repriced_total = total_expr.calc(bound_scope); @@ -573,6 +574,78 @@ void changed_value_posts::output_revaluation(post_t& post, const date_t& date) } } +void changed_value_posts::output_intermediate_prices(post_t& post, + const date_t& current) +{ + // To fix BZ#199, examine the balance of last_post and determine whether the + // price of that amount changed after its date and before the new post's + // date. If so, generate an output_revaluation for that price change. + // Mostly this is only going to occur if the user has a series of pricing + // entries, since a posting-based revaluation would be seen here as a post. + assert(! last_total.is_null()); + + switch (last_total.type()) { + case value_t::INTEGER: + case value_t::SEQUENCE: + break; + case value_t::AMOUNT: + last_total.in_place_cast(value_t::BALANCE); + // fall through... + case value_t::BALANCE: { + commodity_t::history_map all_prices; + + foreach (const balance_t::amounts_map::value_type& amt_comm, + last_total.as_balance().amounts) { + if (optional hist = + amt_comm.first->varied_history()) { + foreach + (const commodity_t::history_by_commodity_map::value_type& comm_hist, + hist->histories) { + foreach (const commodity_t::history_map::value_type& price, + comm_hist.second.prices) { + if (price.first.date() > post.date() && + price.first.date() < current) { + DEBUG("filters.revalued", post.date() << " < " + << price.first.date() << " < " << current); + DEBUG("filters.revalued", "inserting " + << price.second << " at " << price.first.date()); + all_prices.insert(price); + } + } + } + } + } + + // Choose the last price from each day as the price to use + typedef std::map > history_by_date_map; + history_by_date_map all_prices_by_date; + + BOOST_REVERSE_FOREACH + (const commodity_t::history_map::value_type& price, all_prices) { + // This insert will fail if a later price has already been inserted + // for that date. + DEBUG("filters.revalued", + "re-inserting " << price.second << " at " << price.first.date()); + all_prices_by_date.insert(history_by_date_map::value_type + (price.first.date(), price)); + } + + // Go through the time-sorted prices list, outputting a revaluation for + // each price difference. + foreach (const history_by_date_map::value_type& price, all_prices_by_date) { + output_revaluation(post, price.first); + last_total = repriced_total; + } + break; + } + default: + assert(false); + break; + } +} + void changed_value_posts::output_rounding(post_t& post) { bind_scope_t bound_scope(report, post); @@ -612,8 +685,11 @@ void changed_value_posts::output_rounding(post_t& post) void changed_value_posts::operator()(post_t& post) { - if (last_post) + if (last_post) { + if (! for_accounts_report) + output_intermediate_prices(*last_post, post.date()); output_revaluation(*last_post, post.date()); + } if (changed_values_only) post.xdata().add_flags(POST_EXT_DISPLAYED); diff --git a/src/filters.h b/src/filters.h index dd6b3b1a..a66d8c47 100644 --- a/src/filters.h +++ b/src/filters.h @@ -499,6 +499,7 @@ class changed_value_posts : public item_handler post_t * last_post; value_t last_total; value_t last_display_total; + value_t repriced_total; temporaries_t temps; account_t& revalued_account; account_t& rounding_account; @@ -521,6 +522,7 @@ public: virtual void flush(); void output_revaluation(post_t& post, const date_t& current); + void output_intermediate_prices(post_t& post, const date_t& current); void output_rounding(post_t& post); virtual void operator()(post_t& post); diff --git a/test/baseline/opt-price.test b/test/baseline/opt-price.test index 06cc7751..bf302264 100644 --- a/test/baseline/opt-price.test +++ b/test/baseline/opt-price.test @@ -28,7 +28,8 @@ reg --end 2009/06/26 -V equities 08-Jan-01 Purchase Apple shares Equities $2000 $2000 08-Jun-30 Commodities revalued $500 $2500 08-Jun-30 Sell some Apple sha.. Equities $-1250 $1250 -09-Jun-26 Commodities revalued $750 $2000 +09-Jan-31 Commodities revalued $250 $1500 +09-Jun-26 Commodities revalued $500 $2000 >>>2 === 0 reg --end 2009/06/26 -G equities diff --git a/test/regress/D943AE0F.test b/test/regress/D943AE0F.test index 94a26df5..7a2e14d8 100644 --- a/test/regress/D943AE0F.test +++ b/test/regress/D943AE0F.test @@ -1,4 +1,4 @@ -reg -V --end=2009/06/16 +reg -V <<< D 1000.00 EUR @@ -10,6 +10,6 @@ P 2008/04/20 00:00:00 CAD 1.20 EUR >>>1 08-Apr-15 Paid expenses back .. Ex:Cie-Reimbursements 2200.00 EUR 2200.00 EUR Assets:Checking -2200.00 EUR 0 -09-Jun-16 Commodities revalued 200.00 EUR 200.00 EUR +08-Apr-20 Commodities revalued 200.00 EUR 200.00 EUR >>>2 === 0 -- cgit v1.2.3