diff options
author | John Wiegley <johnw@newartisans.com> | 2009-02-22 04:43:34 -0400 |
---|---|---|
committer | John Wiegley <johnw@newartisans.com> | 2009-02-22 04:51:11 -0400 |
commit | e124811d8a31292c06430815683b2e0fe87f7ceb (patch) | |
tree | e165b781aa87402980c3a47a5617794387e81494 | |
parent | 04fd1ae24c3be6f9d3400c55bce90c3fd743e96b (diff) | |
download | fork-ledger-e124811d8a31292c06430815683b2e0fe87f7ceb.tar.gz fork-ledger-e124811d8a31292c06430815683b2e0fe87f7ceb.tar.bz2 fork-ledger-e124811d8a31292c06430815683b2e0fe87f7ceb.zip |
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
-rw-r--r-- | doc/ledger.1 | 1 | ||||
-rw-r--r-- | src/amount.cc | 16 | ||||
-rw-r--r-- | src/amount.h | 3 | ||||
-rw-r--r-- | src/balance.cc | 6 | ||||
-rw-r--r-- | src/balance.h | 6 | ||||
-rw-r--r-- | src/commodity.cc | 27 | ||||
-rw-r--r-- | src/commodity.h | 35 | ||||
-rw-r--r-- | src/report.cc | 34 | ||||
-rw-r--r-- | src/report.h | 20 | ||||
-rw-r--r-- | src/session.cc | 5 | ||||
-rw-r--r-- | src/session.h | 3 | ||||
-rw-r--r-- | src/value.cc | 20 | ||||
-rw-r--r-- | src/value.h | 5 | ||||
-rw-r--r-- | src/xact.cc | 6 | ||||
-rw-r--r-- | test/unit/t_commodity.cc | 10 |
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 |