Added --exchange (-x) option
This is like -V, except it lets you specify the goal commodity to report
in terms of, for example:
reg -x CAD
This commit is contained in:
parent
04fd1ae24c
commit
e124811d8a
15 changed files with 121 additions and 76 deletions
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -509,13 +509,19 @@ void amount_t::in_place_unreduce()
|
|||
}
|
||||
}
|
||||
|
||||
optional<amount_t> amount_t::value(const optional<datetime_t>& moment,
|
||||
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) {
|
||||
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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
/*@}*/
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -353,7 +353,9 @@ public:
|
|||
return *this = temp;
|
||||
}
|
||||
|
||||
optional<balance_t> value(const optional<datetime_t>& moment = none,
|
||||
optional<balance_t>
|
||||
value(const bool primary_only = false,
|
||||
const optional<datetime_t>& moment = none,
|
||||
const optional<commodity_t&>& in_terms_of = none) const;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@
|
|||
|
||||
namespace ledger {
|
||||
|
||||
void commodity_t::base_t::history_t::add_price(const commodity_t& source,
|
||||
void commodity_t::base_t::history_t::add_price(commodity_t& source,
|
||||
const datetime_t& date,
|
||||
const amount_t& price,
|
||||
const bool reflexive)
|
||||
|
|
@ -50,11 +50,14 @@ void commodity_t::base_t::history_t::add_price(const commodity_t& source,
|
|||
assert(result.second);
|
||||
}
|
||||
|
||||
if (reflexive && ! price.commodity().has_flags(COMMODITY_NOMARKET)) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
bool commodity_t::base_t::history_t::remove_price(const datetime_t& date)
|
||||
|
|
@ -68,7 +71,7 @@ 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,
|
||||
add_price(commodity_t& source,
|
||||
const datetime_t& date,
|
||||
const amount_t& price,
|
||||
const bool reflexive)
|
||||
|
|
|
|||
|
|
@ -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,7 +111,7 @@ 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);
|
||||
|
|
@ -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, "");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -120,19 +120,20 @@ 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>(),
|
||||
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&>());
|
||||
|
|
@ -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_);
|
||||
|
|
|
|||
20
src/report.h
20
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
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
20
src/value.cc
20
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;
|
||||
|
|
|
|||
|
|
@ -427,7 +427,8 @@ 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,
|
||||
value_t value(const bool primary_only = false,
|
||||
const optional<datetime_t>& moment = none,
|
||||
const optional<commodity_t&>& in_terms_of = none) const;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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':
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue