summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/ledger.11
-rw-r--r--src/amount.cc16
-rw-r--r--src/amount.h3
-rw-r--r--src/balance.cc6
-rw-r--r--src/balance.h6
-rw-r--r--src/commodity.cc27
-rw-r--r--src/commodity.h35
-rw-r--r--src/report.cc34
-rw-r--r--src/report.h20
-rw-r--r--src/session.cc5
-rw-r--r--src/session.h3
-rw-r--r--src/value.cc20
-rw-r--r--src/value.h5
-rw-r--r--src/xact.cc6
-rw-r--r--test/unit/t_commodity.cc10
15 files changed, 121 insertions, 76 deletions
diff --git a/doc/ledger.1 b/doc/ledger.1
index 68c4a666..68cafc9c 100644
--- a/doc/ledger.1
+++ b/doc/ledger.1
@@ -128,6 +128,7 @@ See \fB\-\-basis\fR.
.It Fl \-end Pq Fl e
.It Fl \-equity
.It Fl \-exact
+.It Fl \-exchange Ar COMM Pq Fl x
.It Fl \-file Ar FILE
.It Fl \-first Ar INT
See \fB\-\-head\fR.
diff --git a/src/amount.cc b/src/amount.cc
index 6fc049b9..84e77f41 100644
--- a/src/amount.cc
+++ b/src/amount.cc
@@ -509,13 +509,19 @@ void amount_t::in_place_unreduce()
}
}
-optional<amount_t> amount_t::value(const optional<datetime_t>& moment,
- const optional<commodity_t&>& in_terms_of) const
+optional<amount_t>
+amount_t::value(const bool primary_only,
+ const optional<datetime_t>& moment,
+ const optional<commodity_t&>& in_terms_of) const
{
if (quantity) {
- optional<price_point_t> point(commodity().find_price(in_terms_of, moment));
- if (point)
- return (point->price * number()).rounded();
+ if (has_commodity() &&
+ (! primary_only || commodity().has_flags(COMMODITY_PRIMARY)) &&
+ (! in_terms_of || commodity() != *in_terms_of)) {
+ optional<price_point_t> point(commodity().find_price(in_terms_of, moment));
+ if (point)
+ return (point->price * number()).rounded();
+ }
} else {
throw_(amount_error, "Cannot determine value of an uninitialized amount");
}
diff --git a/src/amount.h b/src/amount.h
index 730d3569..b6ca6d16 100644
--- a/src/amount.h
+++ b/src/amount.h
@@ -357,7 +357,8 @@ public:
$100.00.
*/
optional<amount_t>
- value(const optional<datetime_t>& moment = none,
+ value(const bool primary_only = true,
+ const optional<datetime_t>& moment = none,
const optional<commodity_t&>& in_terms_of = none) const;
/*@}*/
diff --git a/src/balance.cc b/src/balance.cc
index b8e4e07d..70e83362 100644
--- a/src/balance.cc
+++ b/src/balance.cc
@@ -159,7 +159,8 @@ balance_t& balance_t::operator/=(const amount_t& amt)
}
optional<balance_t>
-balance_t::value(const optional<datetime_t>& moment,
+balance_t::value(const bool primary_only,
+ const optional<datetime_t>& moment,
const optional<commodity_t&>& in_terms_of) const
{
optional<balance_t> temp;
@@ -167,7 +168,8 @@ balance_t::value(const optional<datetime_t>& moment,
foreach (const amounts_map::value_type& pair, amounts) {
if (! temp)
temp = balance_t();
- if (optional<amount_t> val = pair.second.value(moment, in_terms_of))
+ if (optional<amount_t> val = pair.second.value(primary_only, moment,
+ in_terms_of))
*temp += *val;
else
*temp += pair.second;
diff --git a/src/balance.h b/src/balance.h
index 996132e6..e4f54696 100644
--- a/src/balance.h
+++ b/src/balance.h
@@ -353,8 +353,10 @@ public:
return *this = temp;
}
- optional<balance_t> value(const optional<datetime_t>& moment = none,
- const optional<commodity_t&>& in_terms_of = none) const;
+ optional<balance_t>
+ value(const bool primary_only = false,
+ const optional<datetime_t>& moment = none,
+ const optional<commodity_t&>& in_terms_of = none) const;
/**
* Truth tests. An balance may be truth test in two ways:
diff --git a/src/commodity.cc b/src/commodity.cc
index 2ab7612e..57e20891 100644
--- a/src/commodity.cc
+++ b/src/commodity.cc
@@ -33,10 +33,10 @@
namespace ledger {
-void commodity_t::base_t::history_t::add_price(const commodity_t& source,
- const datetime_t& date,
- const amount_t& price,
- const bool reflexive)
+void commodity_t::base_t::history_t::add_price(commodity_t& source,
+ const datetime_t& date,
+ const amount_t& price,
+ const bool reflexive)
{
DEBUG("commodity.prices",
"add_price to " << source << " : " << date << ", " << price);
@@ -50,10 +50,13 @@ void commodity_t::base_t::history_t::add_price(const commodity_t& source,
assert(result.second);
}
- if (reflexive && ! price.commodity().has_flags(COMMODITY_NOMARKET)) {
- amount_t inverse = price.inverted();
- inverse.set_commodity(const_cast<commodity_t&>(source));
- price.commodity().add_price(date, inverse, false);
+ if (reflexive) {
+ if (! price.commodity().has_flags(COMMODITY_NOMARKET)) {
+ amount_t inverse = price.inverted();
+ inverse.set_commodity(const_cast<commodity_t&>(source));
+ price.commodity().add_price(date, inverse, false);
+ }
+ source.add_flags(COMMODITY_PRIMARY);
}
}
@@ -68,10 +71,10 @@ bool commodity_t::base_t::history_t::remove_price(const datetime_t& date)
}
void commodity_t::base_t::varied_history_t::
- add_price(const commodity_t& source,
- const datetime_t& date,
- const amount_t& price,
- const bool reflexive)
+ add_price(commodity_t& source,
+ const datetime_t& date,
+ const amount_t& price,
+ const bool reflexive)
{
optional<history_t&> hist = history(price.commodity());
if (! hist) {
diff --git a/src/commodity.h b/src/commodity.h
index b6ce0c85..4e57ec6f 100644
--- a/src/commodity.h
+++ b/src/commodity.h
@@ -72,13 +72,13 @@ struct price_point_t
* Long.
*/
class commodity_t
- : public delegates_flags<>,
+ : public delegates_flags<uint_least16_t>,
public equality_comparable1<commodity_t, noncopyable>
{
friend class commodity_pool_t;
public:
- class base_t : public noncopyable, public supports_flags<>
+ class base_t : public noncopyable, public supports_flags<uint_least16_t>
{
base_t();
@@ -90,7 +90,7 @@ public:
history_map prices;
ptime last_lookup;
- void add_price(const commodity_t& source,
+ void add_price(commodity_t& source,
const datetime_t& date,
const amount_t& price,
const bool reflexive = true);
@@ -111,14 +111,14 @@ public:
{
history_by_commodity_map histories;
- void add_price(const commodity_t& source,
+ void add_price(commodity_t& source,
const datetime_t& date,
const amount_t& price,
const bool reflexive = true);
bool remove_price(const datetime_t& date, commodity_t& commodity);
optional<price_point_t>
- find_price(const commodity_t& source,
+ find_price(const commodity_t& source,
const optional<commodity_t&>& commodity = none,
const optional<datetime_t>& moment = none,
const optional<datetime_t>& oldest = none
@@ -127,7 +127,7 @@ public:
#endif
) const;
optional<price_point_t>
- find_price(const commodity_t& source,
+ find_price(const commodity_t& source,
const std::vector<commodity_t *>& commodities,
const optional<datetime_t>& moment = none,
const optional<datetime_t>& oldest = none
@@ -142,15 +142,16 @@ public:
history(const std::vector<commodity_t *>& commodities);
};
-#define COMMODITY_STYLE_DEFAULTS 0x00
-#define COMMODITY_STYLE_SUFFIXED 0x01
-#define COMMODITY_STYLE_SEPARATED 0x02
-#define COMMODITY_STYLE_EUROPEAN 0x04
-#define COMMODITY_STYLE_THOUSANDS 0x08
-#define COMMODITY_NOMARKET 0x10
-#define COMMODITY_BUILTIN 0x20
-#define COMMODITY_WALKED 0x40
-#define COMMODITY_KNOWN 0x80
+#define COMMODITY_STYLE_DEFAULTS 0x000
+#define COMMODITY_STYLE_SUFFIXED 0x001
+#define COMMODITY_STYLE_SEPARATED 0x002
+#define COMMODITY_STYLE_EUROPEAN 0x004
+#define COMMODITY_STYLE_THOUSANDS 0x008
+#define COMMODITY_NOMARKET 0x010
+#define COMMODITY_BUILTIN 0x020
+#define COMMODITY_WALKED 0x040
+#define COMMODITY_KNOWN 0x080
+#define COMMODITY_PRIMARY 0x100
string symbol;
amount_t::precision_t precision;
@@ -164,7 +165,7 @@ public:
public:
explicit base_t(const string& _symbol)
- : supports_flags<>(COMMODITY_STYLE_DEFAULTS),
+ : supports_flags<uint_least16_t>(COMMODITY_STYLE_DEFAULTS),
symbol(_symbol), precision(0), searched(false) {
TRACE_CTOR(base_t, "const string&");
}
@@ -191,7 +192,7 @@ public:
public:
explicit commodity_t(commodity_pool_t * _parent,
const shared_ptr<base_t>& _base)
- : delegates_flags<>(*_base.get()), base(_base),
+ : delegates_flags<uint_least16_t>(*_base.get()), base(_base),
parent_(_parent), annotated(false) {
TRACE_CTOR(commodity_t, "");
}
diff --git a/src/report.cc b/src/report.cc
index d498cc85..62fbe503 100644
--- a/src/report.cc
+++ b/src/report.cc
@@ -120,22 +120,23 @@ value_t report_t::fn_display_total(call_scope_t& scope)
return HANDLER(display_total_).expr.calc(scope);
}
-value_t report_t::fn_market_value(call_scope_t& args)
+value_t report_t::fn_market_value(call_scope_t& scope)
{
- interactive_t env(args, "a&ts");
+ interactive_t args(scope, "a&ts");
commodity_t * commodity = NULL;
- if (env.has(2))
- commodity = amount_t::current_pool->find_or_create(env.get<string>(2));
+ if (args.has(2))
+ commodity = amount_t::current_pool->find_or_create(args.get<string>(2));
- DEBUG("report.market", "getting market value of: " << env.value_at(0));
+ DEBUG("report.market", "getting market value of: " << args.value_at(0));
value_t result =
- env.value_at(0).value(env.has(1) ?
- env.get<datetime_t>(1) : optional<datetime_t>(),
- commodity ?
- optional<commodity_t&>(*commodity) :
- optional<commodity_t&>());
+ args.value_at(0).value(! args.has(2),
+ args.has(1) ?
+ args.get<datetime_t>(1) : optional<datetime_t>(),
+ commodity ?
+ optional<commodity_t&>(*commodity) :
+ optional<commodity_t&>());
DEBUG("report.market", "result is: " << result);
return result;
@@ -165,13 +166,13 @@ value_t report_t::fn_quantity(call_scope_t& args)
return args[0].to_amount().number();
}
-value_t report_t::fn_truncate(call_scope_t& args)
+value_t report_t::fn_truncate(call_scope_t& scope)
{
- interactive_t env(args, "v&ll");
+ interactive_t args(scope, "v&ll");
return string_value(format_t::truncate
- (env.get<string>(0),
- env.has(1) && env.get<long>(1) > 0 ? env.get<long>(1) : 0,
- env.has(2) ? env.get<long>(2) : -1));
+ (args.get<string>(0),
+ args.has(1) && args.get<long>(1) > 0 ? args.get<long>(1) : 0,
+ args.has(2) ? args.get<long>(2) : -1));
}
value_t report_t::fn_justify(call_scope_t& scope)
@@ -400,6 +401,7 @@ option_t<report_t> * report_t::lookup_option(const char * p)
else OPT_(end_);
else OPT(equity);
else OPT(exact);
+ else OPT(exchange_);
break;
case 'f':
OPT(flat);
@@ -498,7 +500,7 @@ option_t<report_t> * report_t::lookup_option(const char * p)
else OPT_(wide);
break;
case 'x':
- OPT_CH(comm_as_payee);
+ OPT_CH(exchange_);
break;
case 'y':
OPT_CH(date_format_);
diff --git a/src/report.h b/src/report.h
index 723100f4..02993727 100644
--- a/src/report.h
+++ b/src/report.h
@@ -269,7 +269,7 @@ public:
});
OPTION(report_t, code_as_payee);
- OPTION(report_t, comm_as_payee); // -x
+ OPTION(report_t, comm_as_payee);
OPTION(report_t, code_as_account);
OPTION(report_t, comm_as_account);
OPTION(report_t, color);
@@ -374,6 +374,14 @@ public:
OPTION(report_t, equity);
OPTION(report_t, exact);
+
+ OPTION_(report_t, exchange_, DO_(args) { // -x
+ on_with(args[0]);
+ call_scope_t no_args(*parent);
+ parent->HANDLER(market).parent = parent;
+ parent->HANDLER(market).handler(no_args);
+ });
+
OPTION(report_t, flat);
OPTION(report_t, forecast_while_);
OPTION(report_t, format_); // -F
@@ -402,8 +410,10 @@ public:
OPTION_(report_t, market, DO() { // -V
parent->HANDLER(revalued).on_only();
- parent->HANDLER(display_amount_).set_expr("market(amount_expr)");
- parent->HANDLER(display_total_).set_expr("market(total_expr)");
+ parent->HANDLER(display_amount_)
+ .set_expr("exchange ? market(amount_expr, now, exchange) : market(amount_expr)");
+ parent->HANDLER(display_total_)
+ .set_expr("exchange ? market(total_expr, now, exchange) : market(total_expr)");
});
OPTION_(report_t, monthly, DO() { // -M
@@ -447,11 +457,11 @@ public:
OPTION(report_t, period_sort_);
OPTION__(report_t, plot_amount_format_, CTOR(report_t, plot_amount_format_) {
- on("%(format_date(date, \"%Y-%m-%d\")) %(quantity(scrub(amount)))\n");
+ on("%(format_date(date, \"%Y-%m-%d\")) %(quantity(scrub(display_amount)))\n");
});
OPTION__(report_t, plot_total_format_, CTOR(report_t, plot_total_format_) {
- on("%(format_date(date, \"%Y-%m-%d\")) %(quantity(scrub(total)))\n");
+ on("%(format_date(date, \"%Y-%m-%d\")) %(quantity(scrub(display_total)))\n");
});
OPTION_(report_t, price, DO() { // -I
diff --git a/src/session.cc b/src/session.cc
index 8e505c22..54fd8e4e 100644
--- a/src/session.cc
+++ b/src/session.cc
@@ -235,6 +235,11 @@ expr_t::ptr_op_t session_t::lookup(const string& name)
{
const char * p = name.c_str();
switch (*p) {
+ case 'n':
+ if (is_eq(p, "now"))
+ return MAKE_FUNCTOR(session_t::fn_now);
+ break;
+
case 'o':
if (WANT_OPT()) { p += OPT_PREFIX_LEN;
if (option_t<session_t> * handler = lookup_option(p))
diff --git a/src/session.h b/src/session.h
index cb71c61b..280f8c3e 100644
--- a/src/session.h
+++ b/src/session.h
@@ -98,6 +98,9 @@ public:
clean_accounts();
}
+ value_t fn_now(call_scope_t&) {
+ return CURRENT_TIME();
+ }
value_t fn_today(call_scope_t&) {
return CURRENT_DATE();
}
diff --git a/src/value.cc b/src/value.cc
index 52760535..5c05c437 100644
--- a/src/value.cc
+++ b/src/value.cc
@@ -1137,23 +1137,25 @@ bool value_t::is_zero() const
return false;
}
-value_t value_t::value(const optional<datetime_t>& moment,
+value_t value_t::value(const bool primary_only,
+ const optional<datetime_t>& moment,
const optional<commodity_t&>& in_terms_of) const
{
switch (type()) {
case INTEGER:
return *this;
- case AMOUNT: {
- if (optional<amount_t> val = as_amount().value(moment, in_terms_of))
+ case AMOUNT:
+ if (optional<amount_t> val =
+ as_amount().value(primary_only, moment, in_terms_of))
return *val;
- return false;
- }
- case BALANCE: {
- if (optional<balance_t> bal = as_balance().value(moment, in_terms_of))
+ return *this;
+
+ case BALANCE:
+ if (optional<balance_t> bal =
+ as_balance().value(primary_only, moment, in_terms_of))
return *bal;
- return false;
- }
+ return *this;
default:
break;
diff --git a/src/value.h b/src/value.h
index 546707f0..da6e2ed6 100644
--- a/src/value.h
+++ b/src/value.h
@@ -427,8 +427,9 @@ 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 optional<datetime_t>& moment = none,
- const optional<commodity_t&>& in_terms_of = none) const;
+ value_t value(const bool primary_only = false,
+ const optional<datetime_t>& moment = none,
+ const optional<commodity_t&>& in_terms_of = none) const;
/**
* Truth tests.
diff --git a/src/xact.cc b/src/xact.cc
index ca0b9baf..5818b687 100644
--- a/src/xact.cc
+++ b/src/xact.cc
@@ -159,6 +159,10 @@ namespace {
return string_value(xact.amount.commodity().symbol());
}
+ value_t get_commodity_is_primary(xact_t& xact) {
+ return xact.amount.commodity().has_flags(COMMODITY_PRIMARY);
+ }
+
value_t get_cost(xact_t& xact) {
if (xact.has_xdata() &&
xact.xdata().has_flags(XACT_EXT_COMPOUND)) {
@@ -262,6 +266,8 @@ expr_t::ptr_op_t xact_t::lookup(const string& name)
case 'p':
if (name == "payee")
return WRAP_FUNCTOR(get_wrapper<&get_payee>);
+ else if (name == "primary")
+ return WRAP_FUNCTOR(get_wrapper<&get_commodity_is_primary>);
break;
case 't':
diff --git a/test/unit/t_commodity.cc b/test/unit/t_commodity.cc
index 80e51bcd..9e38c823 100644
--- a/test/unit/t_commodity.cc
+++ b/test/unit/t_commodity.cc
@@ -68,11 +68,11 @@ void CommodityTestCase::testPriceHistory()
cad.add_price(jan17_06, amount_t("$1.11"));
#ifndef NOT_FOR_PYTHON
- optional<amount_t> amt = x1.value(feb28_07sbm);
+ optional<amount_t> amt = x1.value(false, feb28_07sbm);
assertTrue(amt);
assertEqual(amount_t("$1831.83"), *amt);
- amt = x1.value(CURRENT_TIME());
+ amt = x1.value(false, CURRENT_TIME());
assertTrue(amt);
assertEqual(string("$2124.12"), amt->to_string());
#ifdef INTEGER_MATH
@@ -81,18 +81,18 @@ void CommodityTestCase::testPriceHistory()
assertEqual(string("$2124.1220"), amt->to_fullstring());
#endif
- amt = x1.value(CURRENT_TIME(), euro);
+ amt = x1.value(false, CURRENT_TIME(), euro);
assertTrue(amt);
assertEqual(string("EUR 1366.87"), amt->rounded().to_string());
// Add a newer Euro pricing
aapl.add_price(jan17_07, amount_t("EUR 23.00"));
- amt = x1.value(CURRENT_TIME(), euro);
+ amt = x1.value(false, CURRENT_TIME(), euro);
assertTrue(amt);
assertEqual(string("EUR 2302.30"), amt->to_string());
- amt = x1.value(CURRENT_TIME(), cad);
+ amt = x1.value(false, CURRENT_TIME(), cad);
assertTrue(amt);
assertEqual(string("CAD 3223.22"), amt->to_string());
#endif // NOT_FOR_PYTHON