diff options
author | John Wiegley <johnw@newartisans.com> | 2009-11-25 04:32:30 -0500 |
---|---|---|
committer | John Wiegley <johnw@newartisans.com> | 2009-11-25 04:39:59 -0500 |
commit | 2c90c10db17a1e379639f2aa5e7c47fcb351d9f7 (patch) | |
tree | 136adb3762bfbc58f28667b74a9aa2eca4ae431a | |
parent | a7424c1df9b565e77ff25fee46f8a79d2638f42c (diff) | |
download | fork-ledger-2c90c10db17a1e379639f2aa5e7c47fcb351d9f7.tar.gz fork-ledger-2c90c10db17a1e379639f2aa5e7c47fcb351d9f7.tar.bz2 fork-ledger-2c90c10db17a1e379639f2aa5e7c47fcb351d9f7.zip |
Added support for a "fixed" directive
It lets you specify a fixed cost for a duration of a ledger file, for
example:
fixed ecu $2
2008/01/01 income
assets:bank:checking 1 ecu
income:salary
end fixed
This is equivalent to:
2008/01/01 income
assets:bank:checking 1 ecu {=$2}
income:salary
-rw-r--r-- | src/pool.cc | 16 | ||||
-rw-r--r-- | src/pool.h | 3 | ||||
-rw-r--r-- | src/quotes.cc | 8 | ||||
-rw-r--r-- | src/textual.cc | 69 |
4 files changed, 75 insertions, 21 deletions
diff --git a/src/pool.cc b/src/pool.cc index 00f4a3da..70f4eed6 100644 --- a/src/pool.cc +++ b/src/pool.cc @@ -286,7 +286,8 @@ commodity_pool_t::exchange(const amount_t& amount, return breakdown; } -optional<price_point_t> commodity_pool_t::parse_price_directive(char * line) +optional<std::pair<commodity_t *, price_point_t> > +commodity_pool_t::parse_price_directive(char * line, bool do_not_add_price) { char * date_field_ptr = line; char * time_field_ptr = next_element(date_field_ptr); @@ -295,6 +296,7 @@ optional<price_point_t> commodity_pool_t::parse_price_directive(char * line) char * symbol_and_price; datetime_t datetime; + string symbol; if (std::isdigit(time_field_ptr[0])) { symbol_and_price = next_element(time_field_ptr); @@ -307,12 +309,13 @@ optional<price_point_t> commodity_pool_t::parse_price_directive(char * line) datetime = datetime_t(parse_date(date_field)); } else { - symbol_and_price = date_field_ptr; + symbol = date_field_ptr; + symbol_and_price = time_field_ptr; datetime = CURRENT_TIME(); } - string symbol; - commodity_t::parse_symbol(symbol_and_price, symbol); + if (symbol.empty()) + commodity_t::parse_symbol(symbol_and_price, symbol); price_point_t point; point.when = datetime; @@ -323,9 +326,10 @@ optional<price_point_t> commodity_pool_t::parse_price_directive(char * line) if (commodity_t * commodity = find_or_create(symbol)) { DEBUG("commodity.download", "Adding price for " << symbol << ": " << point.when << " " << point.price); - commodity->add_price(point.when, point.price, true); + if (! do_not_add_price) + commodity->add_price(point.when, point.price, true); commodity->add_flags(COMMODITY_KNOWN); - return point; + return std::pair<commodity_t *, price_point_t>(commodity, point); } return none; @@ -123,7 +123,8 @@ public: // Parse commodity prices from a textual representation - optional<price_point_t> parse_price_directive(char * line); + optional<std::pair<commodity_t *, price_point_t> > + parse_price_directive(char * line, bool do_not_add_price = false); commodity_t * parse_price_expression(const std::string& str, diff --git a/src/quotes.cc b/src/quotes.cc index ffe2142a..f892e93f 100644 --- a/src/quotes.cc +++ b/src/quotes.cc @@ -75,7 +75,7 @@ commodity_quote_from_script(commodity_t& commodity, if (char * p = std::strchr(buf, '\n')) *p = '\0'; DEBUG("commodity.download", "downloaded quote: " << buf); - if (optional<price_point_t> point = + if (optional<std::pair<commodity_t *, price_point_t> > point = commodity_pool_t::current_pool->parse_price_directive(buf)) { if (commodity_pool_t::current_pool->price_db) { #if defined(__GNUG__) && __GNUG__ < 3 @@ -86,12 +86,12 @@ commodity_quote_from_script(commodity_t& commodity, std::ios_base::out | std::ios_base::app); #endif database << "P " - << format_datetime(point->when, FMT_WRITTEN) + << format_datetime(point->second.when, FMT_WRITTEN) << " " << commodity.symbol() - << " " << point->price + << " " << point->second.price << std::endl; } - return point; + return point->second; } } else { DEBUG("commodity.download", diff --git a/src/textual.cc b/src/textual.cc index 0eaa5869..cf670cae 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -54,7 +54,8 @@ namespace { static const std::size_t MAX_LINE = 1024; public: - typedef variant<account_t *, string> state_t; + typedef std::pair<commodity_t *, amount_t> fixed_rate_t; + typedef variant<account_t *, string, fixed_rate_t> state_t; std::list<state_t>& state_stack; @@ -99,6 +100,9 @@ namespace { bool front_is_string() { return state_stack.front().type() == typeid(string); } + bool front_is_fixed_rate() { + return state_stack.front().type() == typeid(fixed_rate_t); + } account_t * top_account() { foreach (state_t& state, state_stack) @@ -135,6 +139,7 @@ namespace { void master_account_directive(char * line); void end_directive(char * line); void alias_directive(char * line); + void fixed_directive(char * line); void tag_directive(char * line); void define_directive(char * line); bool general_directive(char * line); @@ -512,7 +517,7 @@ void instance_t::price_conversion_directive(char * line) void instance_t::price_xact_directive(char * line) { - optional<price_point_t> point = + optional<std::pair<commodity_t *, price_point_t> > point = commodity_pool_t::current_pool->parse_price_directive(skip_ws(line + 1)); if (! point) throw parse_error(_("Pricing entry failed to parse")); @@ -705,10 +710,13 @@ void instance_t::end_directive(char * kind) if ((name.empty() || name == "account") && ! front_is_account()) throw_(std::runtime_error, - _("'end account' directive does not match open tag directive")); + _("'end account' directive does not match open directive")); else if (name == "tag" && ! front_is_string()) throw_(std::runtime_error, - _("'end tag' directive does not match open account directive")); + _("'end tag' directive does not match open directive")); + else if (name == "fixed" && ! front_is_fixed_rate()) + throw_(std::runtime_error, + _("'end fixed' directive does not match open directive")); if (state_stack.size() <= 1) throw_(std::runtime_error, @@ -738,6 +746,18 @@ void instance_t::alias_directive(char * line) } } +void instance_t::fixed_directive(char * line) +{ + if (optional<std::pair<commodity_t *, price_point_t> > price_point = + commodity_pool_t::current_pool->parse_price_directive(trim_ws(line), + true)) { + state_stack.push_front(fixed_rate_t(price_point->first, + price_point->second.price)); + } else { + throw_(std::runtime_error, _("Error in fixed directive")); + } +} + void instance_t::tag_directive(char * line) { string tag(trim_ws(line)); @@ -799,6 +819,13 @@ bool instance_t::general_directive(char * line) } break; + case 'f': + if (std::strcmp(p, "fixed") == 0) { + fixed_directive(arg); + return true; + } + break; + case 'i': if (std::strcmp(p, "include") == 0) { include_directive(arg); @@ -812,6 +839,13 @@ bool instance_t::general_directive(char * line) return true; } break; + + case 'y': + if (std::strcmp(p, "year") == 0) { + year_directive(arg); + return true; + } + break; } if (expr_t::ptr_op_t op = lookup(symbol_t::DIRECTIVE, p)) { @@ -935,13 +969,28 @@ post_t * instance_t::parse_post(char * line, post.get(), PARSE_NO_REDUCE | PARSE_SINGLE | PARSE_NO_ASSIGN, defer_expr); - if (! post->amount.is_null() && honor_strict && strict && - post->amount.has_commodity() && + if (! post->amount.is_null() && post->amount.has_commodity()) { + if (honor_strict && strict && ! post->amount.commodity().has_flags(COMMODITY_KNOWN)) { - if (post->_state == item_t::UNCLEARED) - warning_(_("\"%1\", line %2: Unknown commodity '%3'") - << pathname << linenum << post->amount.commodity()); - post->amount.commodity().add_flags(COMMODITY_KNOWN); + if (post->_state == item_t::UNCLEARED) + warning_(_("\"%1\", line %2: Unknown commodity '%3'") + << pathname << linenum << post->amount.commodity()); + post->amount.commodity().add_flags(COMMODITY_KNOWN); + } + + if (! post->amount.has_annotation()) { + foreach (state_t& state, state_stack) { + if (state.type() == typeid(fixed_rate_t)) { + fixed_rate_t& rate(boost::get<fixed_rate_t>(state)); + if (*rate.first == post->amount.commodity()) { + annotation_t details(rate.second); + details.add_flags(ANNOTATION_PRICE_FIXATED); + post->amount.annotate(details); + break; + } + } + } + } } DEBUG("textual.parse", "line " << linenum << ": " |