summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJohn Wiegley <johnw@newartisans.com>2010-06-09 05:44:06 -0400
committerJohn Wiegley <johnw@newartisans.com>2010-06-09 05:44:06 -0400
commit14476a63f4abbdce5311ace031b7345a34af060b (patch)
treec1900d4f2a95db2cdb4cf7404669fe668acedb4a /src
parente8ffbd6f2fb57f2b65f57f92edf8eb444cc2f71f (diff)
parent474d95adeb09b0c13f0e19b336b5b01ee69c6275 (diff)
downloadfork-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.cc57
-rw-r--r--src/amount.h11
-rw-r--r--src/annotate.cc28
-rw-r--r--src/annotate.h6
-rw-r--r--src/balance.cc23
-rw-r--r--src/balance.h14
-rw-r--r--src/chain.cc12
-rw-r--r--src/commodity.cc7
-rw-r--r--src/commodity.h6
-rw-r--r--src/exprbase.h2
-rw-r--r--src/filters.cc187
-rw-r--r--src/filters.h22
-rw-r--r--src/item.cc5
-rw-r--r--src/output.cc2
-rw-r--r--src/post.cc17
-rw-r--r--src/post.h2
-rw-r--r--src/print.cc19
-rw-r--r--src/py_amount.cc6
-rw-r--r--src/py_balance.cc6
-rw-r--r--src/py_value.cc6
-rw-r--r--src/report.cc25
-rw-r--r--src/report.h12
-rw-r--r--src/value.cc39
-rw-r--r--src/value.h13
-rw-r--r--src/xact.cc10
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':
diff --git a/src/post.h b/src/post.h
index ed22634f..51cbad64 100644
--- a/src/post.h
+++ b/src/post.h
@@ -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;