diff options
author | John Wiegley <johnw@newartisans.com> | 2010-06-09 05:44:06 -0400 |
---|---|---|
committer | John Wiegley <johnw@newartisans.com> | 2010-06-09 05:44:06 -0400 |
commit | 14476a63f4abbdce5311ace031b7345a34af060b (patch) | |
tree | c1900d4f2a95db2cdb4cf7404669fe668acedb4a /src | |
parent | e8ffbd6f2fb57f2b65f57f92edf8eb444cc2f71f (diff) | |
parent | 474d95adeb09b0c13f0e19b336b5b01ee69c6275 (diff) | |
download | fork-ledger-14476a63f4abbdce5311ace031b7345a34af060b.tar.gz fork-ledger-14476a63f4abbdce5311ace031b7345a34af060b.tar.bz2 fork-ledger-14476a63f4abbdce5311ace031b7345a34af060b.zip |
Merge branch 'next'
Diffstat (limited to 'src')
-rw-r--r-- | src/amount.cc | 57 | ||||
-rw-r--r-- | src/amount.h | 11 | ||||
-rw-r--r-- | src/annotate.cc | 28 | ||||
-rw-r--r-- | src/annotate.h | 6 | ||||
-rw-r--r-- | src/balance.cc | 23 | ||||
-rw-r--r-- | src/balance.h | 14 | ||||
-rw-r--r-- | src/chain.cc | 12 | ||||
-rw-r--r-- | src/commodity.cc | 7 | ||||
-rw-r--r-- | src/commodity.h | 6 | ||||
-rw-r--r-- | src/exprbase.h | 2 | ||||
-rw-r--r-- | src/filters.cc | 187 | ||||
-rw-r--r-- | src/filters.h | 22 | ||||
-rw-r--r-- | src/item.cc | 5 | ||||
-rw-r--r-- | src/output.cc | 2 | ||||
-rw-r--r-- | src/post.cc | 17 | ||||
-rw-r--r-- | src/post.h | 2 | ||||
-rw-r--r-- | src/print.cc | 19 | ||||
-rw-r--r-- | src/py_amount.cc | 6 | ||||
-rw-r--r-- | src/py_balance.cc | 6 | ||||
-rw-r--r-- | src/py_value.cc | 6 | ||||
-rw-r--r-- | src/report.cc | 25 | ||||
-rw-r--r-- | src/report.h | 12 | ||||
-rw-r--r-- | src/value.cc | 39 | ||||
-rw-r--r-- | src/value.h | 13 | ||||
-rw-r--r-- | src/xact.cc | 10 |
25 files changed, 306 insertions, 231 deletions
diff --git a/src/amount.cc b/src/amount.cc index 7eb94442..105b54ef 100644 --- a/src/amount.cc +++ b/src/amount.cc @@ -724,8 +724,7 @@ void amount_t::in_place_unreduce() } optional<amount_t> -amount_t::value(const bool primary_only, - const optional<datetime_t>& moment, +amount_t::value(const optional<datetime_t>& moment, const optional<commodity_t&>& in_terms_of) const { if (quantity) { @@ -740,35 +739,39 @@ amount_t::value(const bool primary_only, "amount_t::value: in_terms_of = " << in_terms_of->symbol()); #endif if (has_commodity() && - (! primary_only || ! commodity().has_flags(COMMODITY_PRIMARY))) { - if (in_terms_of && - commodity().referent() == in_terms_of->referent()) { + (in_terms_of || ! commodity().has_flags(COMMODITY_PRIMARY))) { + optional<price_point_t> point; + optional<commodity_t&> comm(in_terms_of); + + if (comm && commodity().referent() == comm->referent()) { return *this; } - else if (has_annotation() && annotation().price && - annotation().has_flags(ANNOTATION_PRICE_FIXATED)) { - amount_t price(*annotation().price); + else if (has_annotation() && annotation().price) { + if (annotation().has_flags(ANNOTATION_PRICE_FIXATED)) { + point = price_point_t(); + point->price = *annotation().price; + } + else if (! in_terms_of) { + comm = annotation().price->commodity(); + } + } + + if (! point) { + point = commodity().find_price(comm, moment); + // Whether a price was found or not, check whether we should attempt + // to download a price from the Internet. This is done if (a) no + // price was found, or (b) the price is "stale" according to the + // setting of --price-exp. + if (point) + point = commodity().check_for_updated_price(point, moment, comm); + } + + if (point) { + amount_t price(point->price); price.multiply(*this, true); price.in_place_round(); return price; } - else { - optional<price_point_t> point = - commodity().find_price(in_terms_of, moment); - - // Whether a price was found or not, check whether we should - // attempt to download a price from the Internet. This is done - // if (a) no price was found, or (b) the price is "stale" - // according to the setting of --price-exp. - point = commodity().check_for_updated_price(point, moment, - in_terms_of); - if (point) { - amount_t price(point->price); - price.multiply(*this, true); - price.in_place_round(); - return price; - } - } } } else { throw_(amount_error, @@ -1216,7 +1219,7 @@ void amount_t::parse_conversion(const string& larger_str, smaller.commodity().set_larger(larger); } -void amount_t::print(std::ostream& _out) const +void amount_t::print(std::ostream& _out, const uint_least8_t flags) const { VERIFY(valid()); @@ -1246,7 +1249,7 @@ void amount_t::print(std::ostream& _out) const // If there are any annotations associated with this commodity, output them // now. - comm.write_annotations(out); + comm.write_annotations(out, flags & AMOUNT_PRINT_NO_COMPUTED_ANNOTATIONS); // Things are output to a string first, so that if anyone has specified a // width or fill for _out, it will be applied to the entire amount string, diff --git a/src/amount.h b/src/amount.h index 49f33417..09c9dc49 100644 --- a/src/amount.h +++ b/src/amount.h @@ -399,8 +399,7 @@ public: $100.00. */ optional<amount_t> - value(const bool primary_only = true, - const optional<datetime_t>& moment = none, + value(const optional<datetime_t>& moment = none, const optional<commodity_t&>& in_terms_of = none) const; amount_t price() const; @@ -677,7 +676,13 @@ public: true, the full internal precision of the amount is displayed, regardless of its commodity's display precision. */ - void print(std::ostream& out) const; +#define AMOUNT_PRINT_NO_FLAGS 0x00 +#define AMOUNT_PRINT_RIGHT_JUSTIFY 0x01 +#define AMOUNT_PRINT_COLORIZE 0x02 +#define AMOUNT_PRINT_NO_COMPUTED_ANNOTATIONS 0x04 + + void print(std::ostream& out, + const uint_least8_t flags = AMOUNT_PRINT_NO_FLAGS) const; /*@}*/ diff --git a/src/annotate.cc b/src/annotate.cc index feb3b3ca..e1e64ac2 100644 --- a/src/annotate.cc +++ b/src/annotate.cc @@ -66,15 +66,6 @@ void annotation_t::parse(std::istream& in) temp.parse(buf, PARSE_NO_MIGRATE); DEBUG("commodity.annotations", "Parsed annotation price: " << temp); - - // Since this price will maintain its own precision, make sure - // it is at least as large as the base commodity, since the user - // may have only specified {$1} or something similar. - - if (temp.has_commodity() && - temp.precision() > temp.commodity().precision()) - temp = temp.rounded(); // no need to retain individual precision - price = temp; } else if (c == '[') { @@ -118,18 +109,22 @@ void annotation_t::parse(std::istream& in) #endif } -void annotation_t::print(std::ostream& out, bool keep_base) const +void annotation_t::print(std::ostream& out, bool keep_base, + bool no_computed_annotations) const { - if (price) + if (price && + (! no_computed_annotations || ! has_flags(ANNOTATION_PRICE_CALCULATED))) out << " {" << (has_flags(ANNOTATION_PRICE_FIXATED) ? "=" : "") - << (keep_base ? *price : price->unreduced()).rounded() + << (keep_base ? *price : price->unreduced()) << '}'; - if (date) + if (date && + (! no_computed_annotations || ! has_flags(ANNOTATION_DATE_CALCULATED))) out << " [" << format_date(*date, FMT_WRITTEN) << ']'; - if (tag) + if (tag && + (! no_computed_annotations || ! has_flags(ANNOTATION_TAG_CALCULATED))) out << " (" << *tag << ')'; } @@ -197,9 +192,10 @@ annotated_commodity_t::strip_annotations(const keep_details_t& what_to_keep) return *new_comm; } -void annotated_commodity_t::write_annotations(std::ostream& out) const +void annotated_commodity_t::write_annotations + (std::ostream& out, bool no_computed_annotations) const { - details.print(out, pool().keep_base); + details.print(out, pool().keep_base, no_computed_annotations); } } // namespace ledger diff --git a/src/annotate.h b/src/annotate.h index 6ef26476..4f2f1b04 100644 --- a/src/annotate.h +++ b/src/annotate.h @@ -87,7 +87,8 @@ struct annotation_t : public supports_flags<>, void parse(std::istream& in); - void print(std::ostream& out, bool keep_base = false) const; + void print(std::ostream& out, bool keep_base = false, + bool no_computed_annotations = false) const; bool valid() const { assert(*this); @@ -230,7 +231,8 @@ public: } virtual commodity_t& strip_annotations(const keep_details_t& what_to_keep); - virtual void write_annotations(std::ostream& out) const; + virtual void write_annotations(std::ostream& out, + bool no_computed_annotations = false) const; #if defined(HAVE_BOOST_SERIALIZATION) private: diff --git a/src/balance.cc b/src/balance.cc index 9b39a49a..a8bc649d 100644 --- a/src/balance.cc +++ b/src/balance.cc @@ -185,16 +185,14 @@ balance_t& balance_t::operator/=(const amount_t& amt) } optional<balance_t> -balance_t::value(const bool primary_only, - const optional<datetime_t>& moment, +balance_t::value(const optional<datetime_t>& moment, const optional<commodity_t&>& in_terms_of) const { balance_t temp; bool resolved = false; foreach (const amounts_map::value_type& pair, amounts) { - if (optional<amount_t> val = pair.second.value(primary_only, moment, - in_terms_of)) { + if (optional<amount_t> val = pair.second.value(moment, in_terms_of)) { temp += *val; resolved = true; } else { @@ -252,11 +250,10 @@ balance_t::strip_annotations(const keep_details_t& what_to_keep) const return temp; } -void balance_t::print(std::ostream& out, - const int first_width, - const int latter_width, - const bool right_justify, - const bool colorize) const +void balance_t::print(std::ostream& out, + const int first_width, + const int latter_width, + const uint_least8_t flags) const { bool first = true; int lwidth = latter_width; @@ -285,14 +282,14 @@ void balance_t::print(std::ostream& out, } std::ostringstream buf; - buf << *amount; - justify(out, buf.str(), width, right_justify, - colorize && amount->sign() < 0); + amount->print(buf, flags); + justify(out, buf.str(), width, flags & AMOUNT_PRINT_RIGHT_JUSTIFY, + flags & AMOUNT_PRINT_COLORIZE && amount->sign() < 0); } if (first) { out.width(first_width); - if (right_justify) + if (flags & AMOUNT_PRINT_RIGHT_JUSTIFY) out << std::right; else out << std::left; diff --git a/src/balance.h b/src/balance.h index 5c00c55a..12d7bc02 100644 --- a/src/balance.h +++ b/src/balance.h @@ -384,9 +384,8 @@ public: } optional<balance_t> - value(const bool primary_only = false, - const optional<datetime_t>& moment = none, - const optional<commodity_t&>& in_terms_of = none) const; + value(const optional<datetime_t>& moment = none, + const optional<commodity_t&>& in_terms_of = none) const; balance_t price() const; @@ -530,11 +529,10 @@ public: * relative amounts of those commodities. There is no option to * change this behavior. */ - void print(std::ostream& out, - const int first_width = -1, - const int latter_width = -1, - const bool right_justify = false, - const bool colorize = false) const; + void print(std::ostream& out, + const int first_width = -1, + const int latter_width = -1, + const uint_least8_t flags = AMOUNT_PRINT_NO_FLAGS) const; /** * Debugging methods. There are two methods defined to help with diff --git a/src/chain.cc b/src/chain.cc index 86d35f14..9a74cdca 100644 --- a/src/chain.cc +++ b/src/chain.cc @@ -113,7 +113,7 @@ post_handler_ptr chain_post_handlers(post_handler_ptr base_handler, post_handler_ptr handler(base_handler); predicate_t display_predicate; predicate_t only_predicate; - rounding_error_posts * rounding_handler = NULL; + display_filter_posts * display_filter = NULL; assert(report.HANDLED(amount_)); expr_t& expr(report.HANDLER(amount_).expr); @@ -141,10 +141,10 @@ post_handler_ptr chain_post_handlers(post_handler_ptr base_handler, // changed_value_posts adds virtual posts to the list to account for changes // in market value of commodities, which otherwise would affect the running // total unpredictably. - if (report.HANDLED(revalued) && ! report.HANDLED(no_rounding)) { - rounding_handler = new rounding_error_posts(handler, report); - handler.reset(rounding_handler); - } + display_filter = new display_filter_posts(handler, report, + report.HANDLED(revalued) && + ! report.HANDLED(no_rounding)); + handler.reset(display_filter); // filter_posts will only pass through posts matching the // `display_predicate'. @@ -162,7 +162,7 @@ post_handler_ptr chain_post_handlers(post_handler_ptr base_handler, (! for_accounts_report || report.HANDLED(unrealized))) handler.reset(new changed_value_posts(handler, report, for_accounts_report, report.HANDLED(unrealized), - rounding_handler)); + display_filter)); // calc_posts computes the running total. When this appears will determine, // for example, whether filtered posts are included or excluded from the diff --git a/src/commodity.cc b/src/commodity.cc index e45332b2..9a757395 100644 --- a/src/commodity.cc +++ b/src/commodity.cc @@ -264,10 +264,6 @@ commodity_t::varied_history_t::find_price(const commodity_t& source, if (comm == source) continue; - // Only value secondary commodities in terms of primary ones - if (! commodity && ! comm.has_flags(COMMODITY_PRIMARY)) - continue; - DEBUG_INDENT("commodity.prices.find", indent + 1); DEBUG("commodity.prices.find", "searching for price via commodity '" << comm << "'"); @@ -370,8 +366,7 @@ commodity_t::find_price(const optional<commodity_t&>& commodity, #endif ) const { - if (! has_flags(COMMODITY_WALKED) && base->varied_history && - (commodity || ! has_flags(COMMODITY_PRIMARY))) { + if (! has_flags(COMMODITY_WALKED) && base->varied_history) { optional<base_t::time_and_commodity_t> pair; #if defined(VERIFY_ON) optional<price_point_t> checkpoint; diff --git a/src/commodity.h b/src/commodity.h index d8aad10d..ae7d9d66 100644 --- a/src/commodity.h +++ b/src/commodity.h @@ -59,6 +59,10 @@ struct price_point_t datetime_t when; amount_t price; + price_point_t() {} + price_point_t(datetime_t _when, amount_t _price) + : when(_when), price(_price) {} + bool operator==(const price_point_t& other) const { return when == other.when && price == other.price; } @@ -273,7 +277,7 @@ public: virtual commodity_t& strip_annotations(const keep_details_t&) { return *this; } - virtual void write_annotations(std::ostream&) const {} + virtual void write_annotations(std::ostream&, bool) const {} commodity_pool_t& pool() const { return *parent_; diff --git a/src/exprbase.h b/src/exprbase.h index cf81a0a7..0c096ab4 100644 --- a/src/exprbase.h +++ b/src/exprbase.h @@ -162,6 +162,7 @@ public: } #endif // defined(DEBUG_ON) + DEBUG("expr.calc.when", "Compiling: " << str); compile(scope); #if defined(DEBUG_ON) @@ -172,6 +173,7 @@ public: #endif // defined(DEBUG_ON) } + DEBUG("expr.calc.when", "Calculating: " << str); return real_calc(scope); } diff --git a/src/filters.cc b/src/filters.cc index e5c5275c..1385a3f0 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -289,18 +289,16 @@ void calc_posts::operator()(post_t& post) } namespace { - typedef function<void (post_t&)> post_functor_t; - - void handle_value(const value_t& value, - account_t * account, - xact_t * xact, - temporaries_t& temps, - post_handler_ptr handler, - const date_t& date = date_t(), - const value_t& total = value_t(), - const bool direct_amount = false, - const bool mark_visited = false, - const optional<post_functor_t>& functor = none) + void handle_value(const value_t& value, + account_t * account, + xact_t * xact, + temporaries_t& temps, + post_handler_ptr handler, + const date_t& date = date_t(), + const bool act_date_p = true, + const value_t& total = value_t(), + const bool direct_amount = false, + const bool mark_visited = false) { post_t& post = temps.create_post(*xact, account); post.add_flags(ITEM_GENERATED); @@ -319,8 +317,12 @@ namespace { post_t::xdata_t& xdata(post.xdata()); - if (is_valid(date)) - xdata.date = date; + if (is_valid(date)) { + if (act_date_p) + xdata.date = date; + else + xdata.value_date = date; + } value_t temp(value); @@ -353,9 +355,6 @@ namespace { if (direct_amount) xdata.add_flags(POST_EXT_DIRECT_AMT); - if (functor) - (*functor)(post); - DEBUG("filters.changed_value.rounding", "post.amount = " << post.amount); (*handler)(post); @@ -388,12 +387,15 @@ void collapse_posts::report_subtotal() } else { date_t earliest_date; + date_t latest_date; foreach (post_t * post, component_posts) { - date_t reported = post->date(); - if (! is_valid(earliest_date) || - reported < earliest_date) - earliest_date = reported; + date_t date = post->date(); + date_t value_date = post->value_date(); + if (! is_valid(earliest_date) || date < earliest_date) + earliest_date = date; + if (! is_valid(latest_date) || value_date > latest_date) + latest_date = value_date; } xact_t& xact = temps.create_xact(); @@ -401,12 +403,16 @@ void collapse_posts::report_subtotal() xact._date = (is_valid(earliest_date) ? earliest_date : last_xact->_date); DEBUG("filters.collapse", "Pseudo-xact date = " << *xact._date); - - handle_value(/* value= */ subtotal, - /* account= */ &totals_account, - /* xact= */ &xact, - /* temps= */ temps, - /* handler= */ handler); + DEBUG("filters.collapse", "earliest date = " << earliest_date); + DEBUG("filters.collapse", "latest date = " << latest_date); + + handle_value(/* value= */ subtotal, + /* account= */ &totals_account, + /* xact= */ &xact, + /* temps= */ temps, + /* handler= */ handler, + /* date= */ latest_date, + /* act_date_p= */ false); } component_posts.clear(); @@ -466,27 +472,46 @@ void related_posts::flush() item_handler<post_t>::flush(); } -rounding_error_posts::rounding_error_posts(post_handler_ptr handler, - report_t& _report) +display_filter_posts::display_filter_posts(post_handler_ptr handler, + report_t& _report, + bool _show_rounding) : item_handler<post_t>(handler), report(_report), - rounding_account(temps.create_account(_("<Rounding>"))) + show_rounding(_show_rounding), + rounding_account(temps.create_account(_("<Rounding>"))), + revalued_account(temps.create_account(_("<Revalued>"))) { - TRACE_CTOR(rounding_error_posts, "post_handler_ptr, report_t&"); + TRACE_CTOR(display_filter_posts, + "post_handler_ptr, report_t&, account_t&, bool"); display_amount_expr = report.HANDLER(display_amount_).expr; display_total_expr = report.HANDLER(display_total_).expr; } -void rounding_error_posts::output_rounding(post_t& post) +bool display_filter_posts::output_rounding(post_t& post) { bind_scope_t bound_scope(report, post); - value_t new_display_total(display_total_expr.calc(bound_scope)); + value_t new_display_total; + + if (show_rounding) { + new_display_total = display_total_expr.calc(bound_scope); - DEBUG("filters.changed_value.rounding", - "rounding.new_display_total = " << new_display_total); + DEBUG("filters.changed_value.rounding", + "rounding.new_display_total = " << new_display_total); + } + + // Allow the posting to be displayed if: + // 1. It's display_amount would display as non-zero + // 2. The --empty option was specified + // 3. The account of the posting is <Revalued> + + if (post.account == &revalued_account) { + if (show_rounding) + last_display_total = new_display_total; + return true; + } - if (! last_display_total.is_null()) { - if (value_t repriced_amount = display_amount_expr.calc(bound_scope)) { + if (value_t repriced_amount = display_amount_expr.calc(bound_scope)) { + if (! last_display_total.is_null()) { DEBUG("filters.changed_value.rounding", "rounding.repriced_amount = " << repriced_amount); @@ -502,29 +527,29 @@ void rounding_error_posts::output_rounding(post_t& post) DEBUG("filters.changed_value.rounding", "rounding.diff = " << diff); - xact_t& xact = temps.create_xact(); - xact.payee = _("Commodity rounding"); - xact._date = post.date(); - handle_value(/* value= */ diff, /* account= */ &rounding_account, - /* xact= */ &xact, + /* xact= */ post.xact, /* temps= */ temps, /* handler= */ handler, - /* date= */ *xact._date, + /* date= */ date_t(), + /* act_date_p= */ true, /* total= */ precise_display_total, /* direct_amount= */ true); } - } + } + if (show_rounding) + last_display_total = new_display_total; + return true; + } else { + return report.HANDLED(empty); } - last_display_total = new_display_total; } -void rounding_error_posts::operator()(post_t& post) +void display_filter_posts::operator()(post_t& post) { - output_rounding(post); - - item_handler<post_t>::operator()(post); + if (output_rounding(post)) + item_handler<post_t>::operator()(post); } changed_value_posts::changed_value_posts @@ -532,12 +557,13 @@ changed_value_posts::changed_value_posts report_t& _report, bool _for_accounts_report, bool _show_unrealized, - rounding_error_posts * _rounding_handler) + display_filter_posts * _display_filter) : item_handler<post_t>(handler), report(_report), for_accounts_report(_for_accounts_report), show_unrealized(_show_unrealized), last_post(NULL), - revalued_account(temps.create_account(_("<Revalued>"))), - rounding_handler(_rounding_handler) + revalued_account(_display_filter ? _display_filter->revalued_account : + temps.create_account(_("<Revalued>"))), + display_filter(_display_filter) { TRACE_CTOR(changed_value_posts, "post_handler_ptr, report_t&, bool"); @@ -604,7 +630,7 @@ void changed_value_posts::output_revaluation(post_t& post, const date_t& date) xact_t& xact = temps.create_xact(); xact.payee = _("Commodities revalued"); - xact._date = is_valid(date) ? date : post.date(); + xact._date = is_valid(date) ? date : post.value_date(); if (! for_accounts_report) { handle_value @@ -614,6 +640,7 @@ void changed_value_posts::output_revaluation(post_t& post, const date_t& date) /* temps= */ temps, /* handler= */ handler, /* date= */ *xact._date, + /* act_date_p= */ true, /* total= */ repriced_total); } else if (show_unrealized) { @@ -626,6 +653,7 @@ void changed_value_posts::output_revaluation(post_t& post, const date_t& date) /* temps= */ temps, /* handler= */ handler, /* date= */ *xact._date, + /* act_date_p= */ true, /* total= */ value_t(), /* direct_amount= */ false, /* mark_visited= */ true); @@ -649,7 +677,7 @@ void changed_value_posts::output_intermediate_prices(post_t& post, xact_t& xact(temps.create_xact()); xact.payee = _("Commodities revalued"); - xact._date = is_valid(current) ? current : post.date(); + xact._date = is_valid(current) ? current : post.value_date(); post_t& temp(temps.copy_post(post, xact)); temp.add_flags(ITEM_GENERATED); @@ -711,9 +739,9 @@ void changed_value_posts::output_intermediate_prices(post_t& post, hist->histories) { foreach (const commodity_t::history_map::value_type& price, comm_hist.second.prices) { - if (price.first.date() > post.date() && + if (price.first.date() > post.value_date() && price.first.date() < current) { - DEBUG("filters.revalued", post.date() << " < " + DEBUG("filters.revalued", post.value_date() << " < " << price.first.date() << " < " << current); DEBUG("filters.revalued", "inserting " << price.second << " at " << price.first.date()); @@ -755,8 +783,8 @@ void changed_value_posts::operator()(post_t& post) { if (last_post) { if (! for_accounts_report) - output_intermediate_prices(*last_post, post.date()); - output_revaluation(*last_post, post.date()); + output_intermediate_prices(*last_post, post.value_date()); + output_revaluation(*last_post, post.value_date()); } if (changed_values_only) @@ -780,11 +808,12 @@ void subtotal_posts::report_subtotal(const char * spec_fmt, if (! range_start || ! range_finish) { foreach (post_t * post, component_posts) { - date_t date = post->date(); + date_t date = post->date(); + date_t value_date = post->value_date(); if (! range_start || date < *range_start) range_start = date; - if (! range_finish || date > *range_finish) - range_finish = date; + if (! range_finish || value_date > *range_finish) + range_finish = value_date; } } component_posts.clear(); @@ -806,11 +835,13 @@ void subtotal_posts::report_subtotal(const char * spec_fmt, xact._date = *range_start; foreach (values_map::value_type& pair, values) - handle_value(/* value= */ pair.second.value, - /* account= */ pair.second.account, - /* xact= */ &xact, - /* temps= */ temps, - /* handler= */ handler); + handle_value(/* value= */ pair.second.value, + /* account= */ pair.second.account, + /* xact= */ &xact, + /* temps= */ temps, + /* handler= */ handler, + /* date= */ *range_finish, + /* act_date_p= */ false); values.clear(); } @@ -919,17 +950,21 @@ void posts_as_equity::report_subtotal() if (pair.second.value.is_balance()) { foreach (const balance_t::amounts_map::value_type& amount_pair, pair.second.value.as_balance().amounts) - handle_value(/* value= */ amount_pair.second, - /* account= */ pair.second.account, - /* xact= */ &xact, - /* temps= */ temps, - /* handler= */ handler); + handle_value(/* value= */ amount_pair.second, + /* account= */ pair.second.account, + /* xact= */ &xact, + /* temps= */ temps, + /* handler= */ handler, + /* date= */ finish, + /* act_date_p= */ false); } else { - handle_value(/* value= */ pair.second.value, - /* account= */ pair.second.account, - /* xact= */ &xact, - /* temps= */ temps, - /* handler= */ handler); + handle_value(/* value= */ pair.second.value, + /* account= */ pair.second.account, + /* xact= */ &xact, + /* temps= */ temps, + /* handler= */ handler, + /* date= */ finish, + /* act_date_p= */ false); } total += pair.second.value; } diff --git a/src/filters.h b/src/filters.h index 22b27c5d..3f2f2145 100644 --- a/src/filters.h +++ b/src/filters.h @@ -481,7 +481,7 @@ public: } }; -class rounding_error_posts : public item_handler<post_t> +class display_filter_posts : public item_handler<post_t> { // This filter requires that calc_posts be used at some point // later in the chain. @@ -489,21 +489,25 @@ class rounding_error_posts : public item_handler<post_t> expr_t display_amount_expr; expr_t display_total_expr; report_t& report; + bool show_rounding; value_t last_display_total; temporaries_t temps; account_t& rounding_account; - rounding_error_posts(); + display_filter_posts(); public: - rounding_error_posts(post_handler_ptr handler, - report_t& _report); + account_t& revalued_account; + + display_filter_posts(post_handler_ptr handler, + report_t& _report, + bool _show_rounding); - virtual ~rounding_error_posts() { - TRACE_DTOR(rounding_error_posts); + virtual ~display_filter_posts() { + TRACE_DTOR(display_filter_posts); } - void output_rounding(post_t& post); + bool output_rounding(post_t& post); virtual void operator()(post_t& post); @@ -538,7 +542,7 @@ class changed_value_posts : public item_handler<post_t> account_t * gains_equity_account; account_t * losses_equity_account; - rounding_error_posts * rounding_handler; + display_filter_posts * display_filter; changed_value_posts(); @@ -547,7 +551,7 @@ public: report_t& _report, bool _for_accounts_report, bool _show_unrealized, - rounding_error_posts * _rounding_handler); + display_filter_posts * _display_filter); virtual ~changed_value_posts() { TRACE_DTOR(changed_value_posts); diff --git a/src/item.cc b/src/item.cc index fea73066..6a948ae4 100644 --- a/src/item.cc +++ b/src/item.cc @@ -465,6 +465,11 @@ expr_t::ptr_op_t item_t::lookup(const symbol_t::kind_t kind, return WRAP_FUNCTOR(get_wrapper<&get_uncleared>); break; + case 'v': + if (name == "value_date") + return WRAP_FUNCTOR(get_wrapper<&get_date>); + break; + case 'L': if (name[1] == '\0') return WRAP_FUNCTOR(get_wrapper<&get_actual>); diff --git a/src/output.cc b/src/output.cc index f697dee4..5cef19bc 100644 --- a/src/output.cc +++ b/src/output.cc @@ -224,9 +224,11 @@ format_accounts::mark_accounts(account_t& account, const bool flat) if (account.parent && (account.has_xflags(ACCOUNT_EXT_VISITED) || (! flat && visited > 0))) { bind_scope_t bound_scope(report, account); + call_scope_t call_scope(bound_scope); if ((! flat && to_display > 1) || ((flat || to_display != 1 || account.has_xflags(ACCOUNT_EXT_VISITED)) && + (report.HANDLED(empty) || report.fn_display_total(call_scope)) && disp_pred(bound_scope))) { account.xdata().add_flags(ACCOUNT_EXT_TO_DISPLAY); DEBUG("account.display", "Marking account as TO_DISPLAY"); diff --git a/src/post.cc b/src/post.cc index 7dc15830..e182a731 100644 --- a/src/post.cc +++ b/src/post.cc @@ -78,6 +78,13 @@ optional<string> post_t::get_tag(const mask_t& tag_mask, return none; } +date_t post_t::value_date() const +{ + if (xdata_ && is_valid(xdata_->value_date)) + return xdata_->value_date; + return date(); +} + date_t post_t::date() const { if (xdata_ && is_valid(xdata_->date)) @@ -319,6 +326,14 @@ namespace { return long(post.reported_account()->depth); } + value_t get_value_date(post_t& post) { + if (post.has_xdata()) { + post_t::xdata_t& xdata(post.xdata()); + if (! xdata.value_date.is_not_a_date()) + return xdata.value_date; + } + return post.date(); + } value_t get_datetime(post_t& post) { return post.xdata().datetime; } @@ -479,6 +494,8 @@ expr_t::ptr_op_t post_t::lookup(const symbol_t::kind_t kind, case 'v': if (name == "virtual") return WRAP_FUNCTOR(get_wrapper<&get_virtual>); + else if (name == "value_date") + return WRAP_FUNCTOR(get_wrapper<&get_value_date>); break; case 'x': @@ -106,6 +106,7 @@ public: virtual optional<string> get_tag(const mask_t& tag_mask, const optional<mask_t>& value_mask = none) const; + virtual date_t value_date() const; virtual date_t date() const; virtual date_t actual_date() const; virtual optional<date_t> effective_date() const; @@ -141,6 +142,7 @@ public: value_t total; std::size_t count; date_t date; + date_t value_date; datetime_t datetime; account_t * account; diff --git a/src/print.cc b/src/print.cc index 703e885c..84aa441f 100644 --- a/src/print.cc +++ b/src/print.cc @@ -172,13 +172,14 @@ namespace { if (post->amount_expr) { amt = post->amount_expr->text(); } else { - std::size_t amount_width = + int amount_width = (report.HANDLER(amount_width_).specified ? - report.HANDLER(amount_width_).value.to_long() : 12); + report.HANDLER(amount_width_).value.to_int() : 12); std::ostringstream amt_str; - report.scrub(post->amount) - .print(amt_str, static_cast<int>(amount_width), -1, true); + value_t(post->amount).print(amt_str, amount_width, -1, + AMOUNT_PRINT_RIGHT_JUSTIFY | + AMOUNT_PRINT_NO_COMPUTED_ANNOTATIONS); amt = amt_str.str(); } @@ -190,15 +191,17 @@ namespace { amtbuf << string(2 - (slip + amt_slip), ' '); amtbuf << amt; - if (post->cost && ! post->has_flags(POST_CALCULATED)) { + if (post->cost && + ! post->has_flags(POST_CALCULATED | POST_COST_CALCULATED)) { if (post->has_flags(POST_COST_IN_FULL)) - amtbuf << " @@ " << report.scrub(post->cost->abs()); + amtbuf << " @@ " << post->cost->abs(); else - amtbuf << " @ " << report.scrub((*post->cost / post->amount).abs()); + amtbuf << " @ " + << (*post->cost / post->amount).abs(); } if (post->assigned_amount) - amtbuf << " = " << report.scrub(*post->assigned_amount); + amtbuf << " = " << post->assigned_amount; string trailer = amtbuf.str(); out << trailer; diff --git a/src/py_amount.cc b/src/py_amount.cc index 5afe8c6b..c5962446 100644 --- a/src/py_amount.cc +++ b/src/py_amount.cc @@ -45,16 +45,16 @@ using namespace boost::python; namespace { boost::optional<amount_t> py_value_0(const amount_t& amount) { - return amount.value(false, CURRENT_TIME()); + return amount.value(CURRENT_TIME()); } boost::optional<amount_t> py_value_1(const amount_t& amount, commodity_t& in_terms_of) { - return amount.value(false, CURRENT_TIME(), in_terms_of); + return amount.value(CURRENT_TIME(), in_terms_of); } boost::optional<amount_t> py_value_2(const amount_t& amount, commodity_t& in_terms_of, datetime_t& moment) { - return amount.value(false, moment, in_terms_of); + return amount.value(moment, in_terms_of); } void py_parse_2(amount_t& amount, object in, unsigned char flags) { diff --git a/src/py_balance.cc b/src/py_balance.cc index 7be75444..03a73ff6 100644 --- a/src/py_balance.cc +++ b/src/py_balance.cc @@ -45,16 +45,16 @@ using namespace boost::python; namespace { boost::optional<balance_t> py_value_0(const balance_t& balance) { - return balance.value(false, CURRENT_TIME()); + return balance.value(CURRENT_TIME()); } boost::optional<balance_t> py_value_1(const balance_t& balance, commodity_t& in_terms_of) { - return balance.value(false, CURRENT_TIME(), in_terms_of); + return balance.value(CURRENT_TIME(), in_terms_of); } boost::optional<balance_t> py_value_2(const balance_t& balance, commodity_t& in_terms_of, datetime_t& moment) { - return balance.value(false, moment, in_terms_of); + return balance.value(moment, in_terms_of); } boost::optional<amount_t> diff --git a/src/py_value.cc b/src/py_value.cc index 449320ec..46fa94c3 100644 --- a/src/py_value.cc +++ b/src/py_value.cc @@ -48,16 +48,16 @@ BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(set_string_overloads, set_string, 0, 2) namespace { boost::optional<value_t> py_value_0(const value_t& value) { - return value.value(false, CURRENT_TIME()); + return value.value(CURRENT_TIME()); } boost::optional<value_t> py_value_1(const value_t& value, commodity_t& in_terms_of) { - return value.value(false, CURRENT_TIME(), in_terms_of); + return value.value(CURRENT_TIME(), in_terms_of); } boost::optional<value_t> py_value_2(const value_t& value, commodity_t& in_terms_of, datetime_t& moment) { - return value.value(false, moment, in_terms_of); + return value.value(moment, in_terms_of); } PyObject * py_base_type(value_t& value) diff --git a/src/report.cc b/src/report.cc index 4302187e..662db746 100644 --- a/src/report.cc +++ b/src/report.cc @@ -140,11 +140,6 @@ void report_t::normalize_options(const string& verb) if (verb == "print") HANDLER(limit_).on(string("?normalize"), "actual"); - if (! HANDLED(empty)) - HANDLER(display_).on(string("?normalize"), - string("(post?(display_amount|account=\"") + - _("<Revalued>") + "\"):display_total)"); - if (verb[0] != 'b' && verb[0] != 'r') HANDLER(base).on_only(string("?normalize")); @@ -471,7 +466,7 @@ value_t report_t::fn_market(call_scope_t& scope) /* add_prices= */ false, moment); else - result = args.value_at(0).value(true, moment); + result = args.value_at(0).value(moment); if (! result.is_null()) return result; @@ -602,12 +597,17 @@ value_t report_t::fn_truncated(call_scope_t& scope) value_t report_t::fn_justify(call_scope_t& scope) { interactive_t args(scope, "vl&lbb"); + + uint_least8_t flags(AMOUNT_PRINT_NO_FLAGS); + + if (args.has(3) && args.get<bool>(3)) + flags |= AMOUNT_PRINT_RIGHT_JUSTIFY; + if (args.has(4) && args.get<bool>(4)) + flags |= AMOUNT_PRINT_COLORIZE; + std::ostringstream out; args.value_at(0) - .print(out, args.get<int>(1), - args.has(2) ? args.get<int>(2) : -1, - args.has(3) ? args.get<bool>(3) : false, - args.has(4) ? args.get<bool>(4) : false); + .print(out, args.get<int>(1), args.has(2) ? args.get<int>(2) : -1, flags); return string_value(out.str()); } @@ -1308,6 +1308,11 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, return MAKE_FUNCTOR(report_t::fn_unrounded); break; + case 'v': + if (is_eq(p, "value_date")) + return MAKE_FUNCTOR(report_t::fn_now); + break; + case 'w': if (is_eq(p, "white")) return WRAP_FUNCTOR(fn_white); diff --git a/src/report.h b/src/report.h index e4eaeaaa..f8fdf507 100644 --- a/src/report.h +++ b/src/report.h @@ -586,16 +586,16 @@ public: "use_direct_amount ? amount :" " (is_seq(get_at(amount_expr, 0)) ?" " get_at(get_at(amount_expr, 0), 0) :" - " market(get_at(amount_expr, 0), date, exchange)" + " market(get_at(amount_expr, 0), value_date, exchange)" " - get_at(amount_expr, 1))"); parent->HANDLER(revalued_total_) .set_expr(string("--gain"), - "(market(get_at(total_expr, 0), date, exchange), " + "(market(get_at(total_expr, 0), value_date, exchange), " "get_at(total_expr, 1))"); parent->HANDLER(display_total_) .set_expr(string("--gain"), "use_direct_amount ? total_expr :" - " market(get_at(total_expr, 0), date, exchange)" + " market(get_at(total_expr, 0), value_date, exchange)" " - get_at(total_expr, 1)"); }); @@ -642,9 +642,11 @@ public: OPTION_(report_t, market, DO() { // -V parent->HANDLER(revalued).on_only(string("--market")); parent->HANDLER(display_amount_) - .set_expr(string("--market"), "market(amount_expr, date, exchange)"); + .set_expr(string("--market"), + "market(amount_expr, value_date, exchange)"); parent->HANDLER(display_total_) - .set_expr(string("--market"), "market(total_expr, date, exchange)"); + .set_expr(string("--market"), + "market(total_expr, value_date, exchange)"); }); OPTION(report_t, meta_); diff --git a/src/value.cc b/src/value.cc index e9313f0c..e5ced56e 100644 --- a/src/value.cc +++ b/src/value.cc @@ -1401,8 +1401,7 @@ bool value_t::is_zero() const return false; } -value_t value_t::value(const bool primary_only, - const optional<datetime_t>& moment, +value_t value_t::value(const optional<datetime_t>& moment, const optional<commodity_t&>& in_terms_of) const { switch (type()) { @@ -1411,13 +1410,13 @@ value_t value_t::value(const bool primary_only, case AMOUNT: if (optional<amount_t> val = - as_amount().value(primary_only, moment, in_terms_of)) + as_amount().value(moment, in_terms_of)) return *val; return NULL_VALUE; case BALANCE: if (optional<balance_t> bal = - as_balance().value(primary_only, moment, in_terms_of)) + as_balance().value(moment, in_terms_of)) return *bal; return NULL_VALUE; @@ -1455,7 +1454,7 @@ value_t value_t::exchange_commodities(const std::string& commodities, if (commodity_t * commodity = commodity_pool_t::current_pool->parse_price_expression(p, add_prices, moment)) { - value_t result = value(false, moment, *commodity); + value_t result = value(moment, *commodity); if (! result.is_null()) return result; } @@ -1681,18 +1680,17 @@ value_t value_t::strip_annotations(const keep_details_t& what_to_keep) const return NULL_VALUE; } -void value_t::print(std::ostream& out, - const int first_width, - const int latter_width, - const bool right_justify, - const bool colorize) const +void value_t::print(std::ostream& out, + const int first_width, + const int latter_width, + const uint_least8_t flags) const { if (first_width > 0 && (! is_amount() || as_amount().is_zero()) && ! is_balance() && ! is_string()) { out.width(first_width); - if (right_justify) + if (flags & AMOUNT_PRINT_RIGHT_JUSTIFY) out << std::right; else out << std::left; @@ -1716,8 +1714,9 @@ void value_t::print(std::ostream& out, break; case INTEGER: - if (colorize && as_long() < 0) - justify(out, to_string(), first_width, right_justify, true); + if (flags & AMOUNT_PRINT_COLORIZE && as_long() < 0) + justify(out, to_string(), first_width, + flags & AMOUNT_PRINT_RIGHT_JUSTIFY, true); else out << as_long(); break; @@ -1727,21 +1726,20 @@ void value_t::print(std::ostream& out, out << 0; } else { std::ostringstream buf; - buf << as_amount(); - justify(out, buf.str(), first_width, right_justify, - colorize && as_amount().sign() < 0); + as_amount().print(buf, flags & AMOUNT_PRINT_NO_COMPUTED_ANNOTATIONS); + justify(out, buf.str(), first_width, flags & AMOUNT_PRINT_RIGHT_JUSTIFY, + flags & AMOUNT_PRINT_COLORIZE && as_amount().sign() < 0); } break; } case BALANCE: - as_balance().print(out, first_width, latter_width, right_justify, - colorize); + as_balance().print(out, first_width, latter_width, flags); break; case STRING: if (first_width > 0) - justify(out, as_string(), first_width, right_justify); + justify(out, as_string(), first_width, flags & AMOUNT_PRINT_RIGHT_JUSTIFY); else out << as_string(); break; @@ -1759,8 +1757,7 @@ void value_t::print(std::ostream& out, else out << ", "; - value.print(out, first_width, latter_width, right_justify, - colorize); + value.print(out, first_width, latter_width, flags); } out << ')'; break; diff --git a/src/value.h b/src/value.h index 2e3998f3..cb3b024a 100644 --- a/src/value.h +++ b/src/value.h @@ -476,8 +476,7 @@ public: void in_place_unreduce(); // exists for efficiency's sake // Return the "market value" of a given value at a specific time. - value_t value(const bool primary_only = false, - const optional<datetime_t>& moment = none, + value_t value(const optional<datetime_t>& moment = none, const optional<commodity_t&>& in_terms_of = none) const; value_t price() const; @@ -942,11 +941,11 @@ public: /** * Printing methods. */ - void print(std::ostream& out, - const int first_width = -1, - const int latter_width = -1, - const bool right_justify = false, - const bool colorize = false) const; + void print(std::ostream& out, + const int first_width = -1, + const int latter_width = -1, + const uint_least8_t flags = AMOUNT_PRINT_NO_FLAGS) const; + void dump(std::ostream& out, const bool relaxed = true) const; /** diff --git a/src/xact.cc b/src/xact.cc index 495e6410..77a55c66 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -275,11 +275,11 @@ bool xact_base_t::finalize() datetime_t(date(), time_duration(0, 0, 0, 0))); if (post->amount.has_annotation() && - breakdown.basis_cost.commodity() == - breakdown.final_cost.commodity()) { - if (amount_t gain_loss = (breakdown.basis_cost - - breakdown.final_cost).rounded()) { + breakdown.basis_cost.commodity() == breakdown.final_cost.commodity()) { + if (amount_t gain_loss = breakdown.basis_cost - breakdown.final_cost) { DEBUG("xact.finalize", "gain_loss = " << gain_loss); + gain_loss.in_place_round(); + DEBUG("xact.finalize", "gain_loss rounds to = " << gain_loss); add_or_set_value(balance, gain_loss.reduced()); @@ -293,6 +293,8 @@ bool xact_base_t::finalize() p->set_state(post->state()); add_post(p); DEBUG("xact.finalize", "added gain_loss, balance = " << balance); + } else { + DEBUG("xact.finalize", "gain_loss would have display as zero"); } } else { post->amount = breakdown.amount; |