Split commodity.h/cc into three files
commodity.h - code for commodity_t annotate.h - commodity annotations pool.h - commodity pool management
This commit is contained in:
parent
77d69d0e24
commit
991e3a3eaf
19 changed files with 1004 additions and 794 deletions
|
|
@ -30,6 +30,8 @@ libledger_util_la_LDFLAGS = -release $(VERSION).0
|
|||
libledger_math_la_SOURCES = \
|
||||
src/value.cc \
|
||||
src/balance.cc \
|
||||
src/pool.cc \
|
||||
src/annotate.cc \
|
||||
src/commodity.cc \
|
||||
src/amount.cc
|
||||
|
||||
|
|
@ -94,6 +96,8 @@ pkginclude_HEADERS = \
|
|||
\
|
||||
src/amount.h \
|
||||
src/commodity.h \
|
||||
src/annotate.h \
|
||||
src/pool.h \
|
||||
src/balance.h \
|
||||
src/value.h \
|
||||
\
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@
|
|||
|
||||
#include "amount.h"
|
||||
#include "commodity.h"
|
||||
#include "annotate.h"
|
||||
#include "pool.h"
|
||||
|
||||
namespace ledger {
|
||||
|
||||
|
|
|
|||
205
src/annotate.cc
Normal file
205
src/annotate.cc
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
/*
|
||||
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of New Artisans LLC nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <system.hh>
|
||||
|
||||
#include "amount.h"
|
||||
#include "commodity.h"
|
||||
#include "annotate.h"
|
||||
#include "pool.h"
|
||||
|
||||
namespace ledger {
|
||||
|
||||
void annotation_t::parse(std::istream& in)
|
||||
{
|
||||
do {
|
||||
istream_pos_type pos = in.tellg();
|
||||
|
||||
char buf[256];
|
||||
char c = peek_next_nonws(in);
|
||||
if (c == '{') {
|
||||
if (price)
|
||||
throw_(amount_error, _("Commodity specifies more than one price"));
|
||||
|
||||
in.get(c);
|
||||
c = peek_next_nonws(in);
|
||||
if (c == '=') {
|
||||
in.get(c);
|
||||
add_flags(ANNOTATION_PRICE_FIXATED);
|
||||
}
|
||||
|
||||
READ_INTO(in, buf, 255, c, c != '}');
|
||||
if (c == '}')
|
||||
in.get(c);
|
||||
else
|
||||
throw_(amount_error, _("Commodity price lacks closing brace"));
|
||||
|
||||
amount_t temp;
|
||||
temp.parse(buf, amount_t::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 == '[') {
|
||||
if (date)
|
||||
throw_(amount_error, _("Commodity specifies more than one date"));
|
||||
|
||||
in.get(c);
|
||||
READ_INTO(in, buf, 255, c, c != ']');
|
||||
if (c == ']')
|
||||
in.get(c);
|
||||
else
|
||||
throw_(amount_error, _("Commodity date lacks closing bracket"));
|
||||
|
||||
date = parse_date(buf);
|
||||
}
|
||||
else if (c == '(') {
|
||||
if (tag)
|
||||
throw_(amount_error, _("Commodity specifies more than one tag"));
|
||||
|
||||
in.get(c);
|
||||
READ_INTO(in, buf, 255, c, c != ')');
|
||||
if (c == ')')
|
||||
in.get(c);
|
||||
else
|
||||
throw_(amount_error, _("Commodity tag lacks closing parenthesis"));
|
||||
|
||||
tag = buf;
|
||||
}
|
||||
else {
|
||||
in.clear();
|
||||
in.seekg(pos, std::ios::beg);
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
|
||||
#if defined(DEBUG_ON)
|
||||
if (SHOW_DEBUG("amounts.commodities") && *this) {
|
||||
DEBUG("amounts.commodities",
|
||||
"Parsed commodity annotations: " << std::endl << *this);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void annotation_t::print(std::ostream& out, bool keep_base) const
|
||||
{
|
||||
if (price)
|
||||
out << " {"
|
||||
<< (has_flags(ANNOTATION_PRICE_FIXATED) ? "=" : "")
|
||||
<< (keep_base ? *price : price->unreduced()).rounded()
|
||||
<< '}';
|
||||
|
||||
if (date)
|
||||
out << " [" << format_date(*date, string("%Y/%m/%d")) << ']';
|
||||
|
||||
if (tag)
|
||||
out << " (" << *tag << ')';
|
||||
}
|
||||
|
||||
bool keep_details_t::keep_all(const commodity_t& comm) const
|
||||
{
|
||||
return (! comm.annotated ||
|
||||
(keep_price && keep_date && keep_tag && ! only_actuals));
|
||||
}
|
||||
|
||||
bool keep_details_t::keep_any(const commodity_t& comm) const
|
||||
{
|
||||
return comm.annotated && (keep_price || keep_date || keep_tag);
|
||||
}
|
||||
|
||||
bool annotated_commodity_t::operator==(const commodity_t& comm) const
|
||||
{
|
||||
// If the base commodities don't match, the game's up.
|
||||
if (base != comm.base)
|
||||
return false;
|
||||
|
||||
assert(annotated);
|
||||
if (! comm.annotated)
|
||||
return false;
|
||||
|
||||
if (details != as_annotated_commodity(comm).details)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
commodity_t&
|
||||
annotated_commodity_t::strip_annotations(const keep_details_t& what_to_keep)
|
||||
{
|
||||
DEBUG("commodity.annotated.strip",
|
||||
"Reducing commodity " << *this << std::endl
|
||||
<< " keep price " << what_to_keep.keep_price << " "
|
||||
<< " keep date " << what_to_keep.keep_date << " "
|
||||
<< " keep tag " << what_to_keep.keep_tag);
|
||||
|
||||
commodity_t * new_comm;
|
||||
|
||||
bool keep_price = (what_to_keep.keep_price &&
|
||||
(! what_to_keep.only_actuals ||
|
||||
! details.has_flags(ANNOTATION_PRICE_CALCULATED)));
|
||||
bool keep_date = (what_to_keep.keep_date &&
|
||||
(! what_to_keep.only_actuals ||
|
||||
! details.has_flags(ANNOTATION_DATE_CALCULATED)));
|
||||
bool keep_tag = (what_to_keep.keep_tag &&
|
||||
(! what_to_keep.only_actuals ||
|
||||
! details.has_flags(ANNOTATION_TAG_CALCULATED)));
|
||||
|
||||
if ((keep_price && details.price) ||
|
||||
(keep_date && details.date) ||
|
||||
(keep_tag && details.tag))
|
||||
{
|
||||
new_comm = parent().find_or_create
|
||||
(referent(), annotation_t(keep_price ? details.price : none,
|
||||
keep_date ? details.date : none,
|
||||
keep_tag ? details.tag : none));
|
||||
} else {
|
||||
new_comm = parent().find_or_create(base_symbol());
|
||||
}
|
||||
|
||||
assert(new_comm);
|
||||
return *new_comm;
|
||||
}
|
||||
|
||||
void annotated_commodity_t::write_annotations(std::ostream& out) const
|
||||
{
|
||||
details.print(out, parent().keep_base);
|
||||
}
|
||||
|
||||
} // namespace ledger
|
||||
199
src/annotate.h
Normal file
199
src/annotate.h
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
/*
|
||||
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of New Artisans LLC nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup math
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file annotate.h
|
||||
* @author John Wiegley
|
||||
*
|
||||
* @ingroup math
|
||||
*
|
||||
* @brief Types for annotating commodities
|
||||
*
|
||||
* Long.
|
||||
*/
|
||||
#ifndef _ANNOTATE_H
|
||||
#define _ANNOTATE_H
|
||||
|
||||
namespace ledger {
|
||||
|
||||
/**
|
||||
* @brief Brief
|
||||
*
|
||||
* Long.
|
||||
*/
|
||||
struct annotation_t : public supports_flags<>,
|
||||
public equality_comparable<annotation_t>
|
||||
{
|
||||
#define ANNOTATION_PRICE_CALCULATED 0x01
|
||||
#define ANNOTATION_PRICE_FIXATED 0x02
|
||||
#define ANNOTATION_DATE_CALCULATED 0x04
|
||||
#define ANNOTATION_TAG_CALCULATED 0x08
|
||||
|
||||
optional<amount_t> price;
|
||||
optional<date_t> date;
|
||||
optional<string> tag;
|
||||
|
||||
explicit annotation_t(const optional<amount_t>& _price = none,
|
||||
const optional<date_t>& _date = none,
|
||||
const optional<string>& _tag = none)
|
||||
: supports_flags<>(), price(_price), date(_date), tag(_tag) {
|
||||
TRACE_CTOR(annotation_t, "const optional<amount_t>& + date_t + string");
|
||||
}
|
||||
annotation_t(const annotation_t& other)
|
||||
: supports_flags<>(other.flags()),
|
||||
price(other.price), date(other.date), tag(other.tag) {
|
||||
TRACE_CTOR(annotation_t, "copy");
|
||||
}
|
||||
~annotation_t() {
|
||||
TRACE_DTOR(annotation_t);
|
||||
}
|
||||
|
||||
operator bool() const {
|
||||
return price || date || tag;
|
||||
}
|
||||
|
||||
bool operator==(const annotation_t& rhs) const {
|
||||
return (price == rhs.price &&
|
||||
date == rhs.date &&
|
||||
tag == rhs.tag);
|
||||
}
|
||||
|
||||
void parse(std::istream& in);
|
||||
|
||||
void print(std::ostream& out, bool keep_base = false) const;
|
||||
|
||||
bool valid() const {
|
||||
assert(*this);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct keep_details_t
|
||||
{
|
||||
bool keep_price;
|
||||
bool keep_date;
|
||||
bool keep_tag;
|
||||
bool only_actuals;
|
||||
|
||||
explicit keep_details_t(bool _keep_price = false,
|
||||
bool _keep_date = false,
|
||||
bool _keep_tag = false,
|
||||
bool _only_actuals = false)
|
||||
: keep_price(_keep_price),
|
||||
keep_date(_keep_date),
|
||||
keep_tag(_keep_tag),
|
||||
only_actuals(_only_actuals)
|
||||
{
|
||||
TRACE_CTOR(keep_details_t, "bool, bool, bool, bool");
|
||||
}
|
||||
keep_details_t(const keep_details_t& other)
|
||||
: keep_price(other.keep_price), keep_date(other.keep_date),
|
||||
keep_tag(other.keep_tag), only_actuals(other.only_actuals) {
|
||||
TRACE_CTOR(keep_details_t, "copy");
|
||||
}
|
||||
~keep_details_t() throw() {
|
||||
TRACE_DTOR(keep_details_t);
|
||||
}
|
||||
|
||||
bool keep_all() const {
|
||||
return keep_price && keep_date && keep_tag && ! only_actuals;
|
||||
}
|
||||
bool keep_all(const commodity_t& comm) const;
|
||||
|
||||
bool keep_any() const {
|
||||
return keep_price || keep_date || keep_tag;
|
||||
}
|
||||
bool keep_any(const commodity_t& comm) const;
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& out,
|
||||
const annotation_t& details) {
|
||||
details.print(out);
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Brief
|
||||
*
|
||||
* Long.
|
||||
*/
|
||||
class annotated_commodity_t
|
||||
: public commodity_t,
|
||||
public equality_comparable<annotated_commodity_t,
|
||||
equality_comparable2<annotated_commodity_t, commodity_t,
|
||||
noncopyable> >
|
||||
{
|
||||
public:
|
||||
commodity_t * ptr;
|
||||
annotation_t details;
|
||||
|
||||
explicit annotated_commodity_t(commodity_t * _ptr,
|
||||
const annotation_t& _details)
|
||||
: commodity_t(_ptr->parent_, _ptr->base), ptr(_ptr), details(_details) {
|
||||
TRACE_CTOR(annotated_commodity_t, "");
|
||||
annotated = true;
|
||||
}
|
||||
virtual ~annotated_commodity_t() {
|
||||
TRACE_DTOR(annotated_commodity_t);
|
||||
}
|
||||
|
||||
virtual bool operator==(const commodity_t& comm) const;
|
||||
virtual bool operator==(const annotated_commodity_t& comm) const {
|
||||
return *this == static_cast<const commodity_t&>(comm);
|
||||
}
|
||||
|
||||
virtual commodity_t& referent() {
|
||||
return *ptr;
|
||||
}
|
||||
virtual const commodity_t& referent() const {
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
virtual commodity_t& strip_annotations(const keep_details_t& what_to_keep);
|
||||
virtual void write_annotations(std::ostream& out) const;
|
||||
};
|
||||
|
||||
inline annotated_commodity_t&
|
||||
as_annotated_commodity(commodity_t& commodity) {
|
||||
return downcast<annotated_commodity_t>(commodity);
|
||||
}
|
||||
inline const annotated_commodity_t&
|
||||
as_annotated_commodity(const commodity_t& commodity) {
|
||||
return downcast<const annotated_commodity_t>(commodity);
|
||||
}
|
||||
|
||||
} // namespace ledger
|
||||
|
||||
#endif // _ANNOTATE_H
|
||||
|
|
@ -33,6 +33,7 @@
|
|||
|
||||
#include "balance.h"
|
||||
#include "commodity.h"
|
||||
#include "pool.h"
|
||||
#include "unistring.h" // for justify()
|
||||
|
||||
namespace ledger {
|
||||
|
|
|
|||
563
src/commodity.cc
563
src/commodity.cc
|
|
@ -33,13 +33,11 @@
|
|||
|
||||
#include "amount.h"
|
||||
#include "commodity.h"
|
||||
#include "annotate.h"
|
||||
#include "pool.h"
|
||||
|
||||
namespace ledger {
|
||||
|
||||
optional<path> commodity_t::price_db;
|
||||
long commodity_t::download_leeway = 86400;
|
||||
bool commodity_t::download_quotes;
|
||||
|
||||
void commodity_t::base_t::history_t::add_price(commodity_t& source,
|
||||
const datetime_t& date,
|
||||
const amount_t& price,
|
||||
|
|
@ -99,8 +97,9 @@ void commodity_t::base_t::varied_history_t::
|
|||
hist->add_price(source, date, price, reflexive);
|
||||
}
|
||||
|
||||
bool commodity_t::base_t::varied_history_t::remove_price(const datetime_t& date,
|
||||
commodity_t& comm)
|
||||
bool commodity_t::base_t::varied_history_t::
|
||||
remove_price(const datetime_t& date,
|
||||
commodity_t& comm)
|
||||
{
|
||||
DEBUG("commodity.prices.add", "varied_remove_price: " << date << ", " << comm);
|
||||
|
||||
|
|
@ -109,104 +108,6 @@ bool commodity_t::base_t::varied_history_t::remove_price(const datetime_t& date
|
|||
return false;
|
||||
}
|
||||
|
||||
optional<price_point_t> commodity_t::parse_commodity_price(char * line)
|
||||
{
|
||||
char * date_field_ptr = line;
|
||||
char * time_field_ptr = next_element(date_field_ptr);
|
||||
if (! time_field_ptr) return none;
|
||||
string date_field = date_field_ptr;
|
||||
|
||||
char * symbol_and_price;
|
||||
datetime_t datetime;
|
||||
|
||||
if (std::isdigit(time_field_ptr[0])) {
|
||||
symbol_and_price = next_element(time_field_ptr);
|
||||
if (! symbol_and_price) return none;
|
||||
datetime = parse_datetime(date_field + " " + time_field_ptr);
|
||||
} else {
|
||||
symbol_and_price = time_field_ptr;
|
||||
datetime = parse_datetime(date_field);
|
||||
}
|
||||
|
||||
string symbol;
|
||||
parse_symbol(symbol_and_price, symbol);
|
||||
|
||||
price_point_t point;
|
||||
point.when = datetime;
|
||||
point.price.parse(symbol_and_price);
|
||||
VERIFY(point.price.valid());
|
||||
|
||||
if (commodity_t * commodity =
|
||||
amount_t::current_pool->find_or_create(symbol)) {
|
||||
commodity->add_price(point.when, point.price, true);
|
||||
commodity->add_flags(COMMODITY_KNOWN);
|
||||
return point;
|
||||
}
|
||||
|
||||
return none;
|
||||
}
|
||||
|
||||
|
||||
optional<price_point_t>
|
||||
commodity_t::download_quote(const optional<commodity_t&>& commodity) const
|
||||
{
|
||||
DEBUG("commodity.download", "downloading quote for symbol " << symbol());
|
||||
#if defined(DEBUG_ON)
|
||||
if (commodity)
|
||||
DEBUG("commodity.download",
|
||||
" in terms of commodity " << commodity->symbol());
|
||||
#endif
|
||||
|
||||
char buf[256];
|
||||
buf[0] = '\0';
|
||||
|
||||
string getquote_cmd("getquote \"");
|
||||
getquote_cmd += symbol();
|
||||
getquote_cmd += "\" \"";
|
||||
if (commodity)
|
||||
getquote_cmd += commodity->symbol();
|
||||
getquote_cmd += "\"";
|
||||
|
||||
DEBUG("commodity.download", "invoking command: " << getquote_cmd);
|
||||
|
||||
bool success = true;
|
||||
if (FILE * fp = popen(getquote_cmd.c_str(), "r")) {
|
||||
if (std::feof(fp) || ! std::fgets(buf, 255, fp))
|
||||
success = false;
|
||||
if (pclose(fp) != 0)
|
||||
success = false;
|
||||
} else {
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (success && buf[0]) {
|
||||
char * p = std::strchr(buf, '\n');
|
||||
if (p) *p = '\0';
|
||||
|
||||
DEBUG("commodity.download", "downloaded quote: " << buf);
|
||||
|
||||
optional<price_point_t> point = parse_commodity_price(buf);
|
||||
|
||||
if (point) {
|
||||
if (price_db) {
|
||||
#if defined(__GNUG__) && __GNUG__ < 3
|
||||
ofstream database(*price_db, ios::out | ios::app);
|
||||
#else
|
||||
ofstream database(*price_db, std::ios_base::out | std::ios_base::app);
|
||||
#endif
|
||||
database << "P " << format_datetime(point->when, string("%Y/%m/%d %H:%M:%S"))
|
||||
<< " " << symbol() << " " << point->price << std::endl;
|
||||
}
|
||||
return point;
|
||||
}
|
||||
} else {
|
||||
throw_(std::runtime_error,
|
||||
_("Failed to download price for '%1' (command: \"getquote %2\")")
|
||||
<< symbol() << symbol());
|
||||
}
|
||||
return none;
|
||||
}
|
||||
|
||||
optional<price_point_t>
|
||||
commodity_t::base_t::history_t::
|
||||
find_price(const optional<datetime_t>& moment,
|
||||
|
|
@ -445,27 +346,27 @@ optional<price_point_t>
|
|||
" found price " << best.price << " from " << best.when);
|
||||
DEBUG("commodity.download",
|
||||
"found price " << best.price << " from " << best.when);
|
||||
if (moment)
|
||||
DEBUG("commodity.download", "moment = " << *moment);
|
||||
DEBUG("commodity.download", "leeway = " << download_leeway);
|
||||
if (moment)
|
||||
DEBUG("commodity.download",
|
||||
"slip.moment = " << (*moment - best.when).total_seconds());
|
||||
else
|
||||
DEBUG("commodity.download",
|
||||
"slip.now = " << (CURRENT_TIME() - best.when).total_seconds());
|
||||
#endif
|
||||
if (download_quotes &&
|
||||
! source.has_flags(COMMODITY_NOMARKET) &&
|
||||
((! moment &&
|
||||
(CURRENT_TIME() - best.when).total_seconds() > download_leeway) ||
|
||||
(moment &&
|
||||
(*moment - best.when).total_seconds() > download_leeway))) {
|
||||
#if 0
|
||||
DEBUG("commodity.download", "leeway = " << download_leeway);
|
||||
datetime_t::sec_type seconds_diff;
|
||||
if (moment) {
|
||||
seconds_diff = (*moment - best.when).total_seconds();
|
||||
DEBUG("commodity.download", "moment = " << *moment);
|
||||
DEBUG("commodity.download", "slip.moment = " << seconds_diff);
|
||||
} else {
|
||||
seconds_diff = (CURRENT_TIME() - best.when).total_seconds();
|
||||
DEBUG("commodity.download", "slip.now = " << seconds_diff);
|
||||
}
|
||||
|
||||
if (download_quotes && ! source.has_flags(COMMODITY_NOMARKET) &&
|
||||
seconds_diff > download_leeway) {
|
||||
DEBUG("commodity.download",
|
||||
"attempting to download a more current quote...");
|
||||
if (optional<price_point_t> quote = source.download_quote(commodity))
|
||||
return quote;
|
||||
}
|
||||
#endif
|
||||
return best;
|
||||
}
|
||||
return none;
|
||||
|
|
@ -497,82 +398,6 @@ optional<commodity_t::base_t::history_t&>
|
|||
return none;
|
||||
}
|
||||
|
||||
void commodity_t::exchange(commodity_t& commodity,
|
||||
const amount_t& per_unit_cost,
|
||||
const datetime_t& moment)
|
||||
{
|
||||
DEBUG("commodity.prices.add", "exchanging commodity " << commodity
|
||||
<< " at per unit cost " << per_unit_cost << " on " << moment);
|
||||
|
||||
commodity_t& base_commodity
|
||||
(commodity.annotated ?
|
||||
as_annotated_commodity(commodity).referent() : commodity);
|
||||
|
||||
base_commodity.add_price(moment, per_unit_cost);
|
||||
}
|
||||
|
||||
commodity_t::cost_breakdown_t
|
||||
commodity_t::exchange(const amount_t& amount,
|
||||
const amount_t& cost,
|
||||
const bool is_per_unit,
|
||||
const optional<datetime_t>& moment,
|
||||
const optional<string>& tag)
|
||||
{
|
||||
DEBUG("commodity.prices.add", "exchange: " << amount << " for " << cost);
|
||||
DEBUG("commodity.prices.add", "exchange: is-per-unit = " << is_per_unit);
|
||||
#if defined(DEBUG_ON)
|
||||
if (moment)
|
||||
DEBUG("commodity.prices.add", "exchange: moment = " << *moment);
|
||||
if (tag)
|
||||
DEBUG("commodity.prices.add", "exchange: tag = " << *tag);
|
||||
#endif
|
||||
|
||||
commodity_t& commodity(amount.commodity());
|
||||
|
||||
annotation_t * current_annotation = NULL;
|
||||
if (commodity.annotated)
|
||||
current_annotation = &as_annotated_commodity(commodity).details;
|
||||
|
||||
amount_t per_unit_cost =
|
||||
(is_per_unit || amount.is_realzero() ? cost : cost / amount).abs();
|
||||
|
||||
DEBUG("commodity.prices.add", "exchange: per-unit-cost = " << per_unit_cost);
|
||||
|
||||
if (! per_unit_cost.is_realzero())
|
||||
exchange(commodity, per_unit_cost, moment ? *moment : CURRENT_TIME());
|
||||
|
||||
cost_breakdown_t breakdown;
|
||||
breakdown.final_cost = ! is_per_unit ? cost : cost * amount;
|
||||
|
||||
DEBUG("commodity.prices.add",
|
||||
"exchange: final-cost = " << breakdown.final_cost);
|
||||
|
||||
if (current_annotation && current_annotation->price)
|
||||
breakdown.basis_cost
|
||||
= (*current_annotation->price * amount).unrounded();
|
||||
else
|
||||
breakdown.basis_cost = breakdown.final_cost;
|
||||
|
||||
DEBUG("commodity.prices.add",
|
||||
"exchange: basis-cost = " << breakdown.basis_cost);
|
||||
|
||||
annotation_t annotation(per_unit_cost, moment ?
|
||||
moment->date() : optional<date_t>(), tag);
|
||||
|
||||
annotation.add_flags(ANNOTATION_PRICE_CALCULATED);
|
||||
if (moment)
|
||||
annotation.add_flags(ANNOTATION_DATE_CALCULATED);
|
||||
if (tag)
|
||||
annotation.add_flags(ANNOTATION_TAG_CALCULATED);
|
||||
|
||||
breakdown.amount = amount_t(amount, annotation);
|
||||
|
||||
DEBUG("commodity.prices.add",
|
||||
"exchange: amount = " << breakdown.amount);
|
||||
|
||||
return breakdown;
|
||||
}
|
||||
|
||||
commodity_t::operator bool() const
|
||||
{
|
||||
return this != parent().null_commodity;
|
||||
|
|
@ -752,159 +577,6 @@ bool commodity_t::valid() const
|
|||
return true;
|
||||
}
|
||||
|
||||
void annotation_t::parse(std::istream& in)
|
||||
{
|
||||
do {
|
||||
istream_pos_type pos = in.tellg();
|
||||
|
||||
char buf[256];
|
||||
char c = peek_next_nonws(in);
|
||||
if (c == '{') {
|
||||
if (price)
|
||||
throw_(amount_error, _("Commodity specifies more than one price"));
|
||||
|
||||
in.get(c);
|
||||
c = peek_next_nonws(in);
|
||||
if (c == '=') {
|
||||
in.get(c);
|
||||
add_flags(ANNOTATION_PRICE_FIXATED);
|
||||
}
|
||||
|
||||
READ_INTO(in, buf, 255, c, c != '}');
|
||||
if (c == '}')
|
||||
in.get(c);
|
||||
else
|
||||
throw_(amount_error, _("Commodity price lacks closing brace"));
|
||||
|
||||
amount_t temp;
|
||||
temp.parse(buf, amount_t::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 == '[') {
|
||||
if (date)
|
||||
throw_(amount_error, _("Commodity specifies more than one date"));
|
||||
|
||||
in.get(c);
|
||||
READ_INTO(in, buf, 255, c, c != ']');
|
||||
if (c == ']')
|
||||
in.get(c);
|
||||
else
|
||||
throw_(amount_error, _("Commodity date lacks closing bracket"));
|
||||
|
||||
date = parse_date(buf);
|
||||
}
|
||||
else if (c == '(') {
|
||||
if (tag)
|
||||
throw_(amount_error, _("Commodity specifies more than one tag"));
|
||||
|
||||
in.get(c);
|
||||
READ_INTO(in, buf, 255, c, c != ')');
|
||||
if (c == ')')
|
||||
in.get(c);
|
||||
else
|
||||
throw_(amount_error, _("Commodity tag lacks closing parenthesis"));
|
||||
|
||||
tag = buf;
|
||||
}
|
||||
else {
|
||||
in.clear();
|
||||
in.seekg(pos, std::ios::beg);
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
|
||||
#if defined(DEBUG_ON)
|
||||
if (SHOW_DEBUG("amounts.commodities") && *this) {
|
||||
DEBUG("amounts.commodities",
|
||||
"Parsed commodity annotations: " << std::endl << *this);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool annotated_commodity_t::operator==(const commodity_t& comm) const
|
||||
{
|
||||
// If the base commodities don't match, the game's up.
|
||||
if (base != comm.base)
|
||||
return false;
|
||||
|
||||
assert(annotated);
|
||||
if (! comm.annotated)
|
||||
return false;
|
||||
|
||||
if (details != as_annotated_commodity(comm).details)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
commodity_t&
|
||||
annotated_commodity_t::strip_annotations(const keep_details_t& what_to_keep)
|
||||
{
|
||||
DEBUG("commodity.annotated.strip",
|
||||
"Reducing commodity " << *this << std::endl
|
||||
<< " keep price " << what_to_keep.keep_price << " "
|
||||
<< " keep date " << what_to_keep.keep_date << " "
|
||||
<< " keep tag " << what_to_keep.keep_tag);
|
||||
|
||||
commodity_t * new_comm;
|
||||
|
||||
bool keep_price = (what_to_keep.keep_price &&
|
||||
(! what_to_keep.only_actuals ||
|
||||
! details.has_flags(ANNOTATION_PRICE_CALCULATED)));
|
||||
bool keep_date = (what_to_keep.keep_date &&
|
||||
(! what_to_keep.only_actuals ||
|
||||
! details.has_flags(ANNOTATION_DATE_CALCULATED)));
|
||||
bool keep_tag = (what_to_keep.keep_tag &&
|
||||
(! what_to_keep.only_actuals ||
|
||||
! details.has_flags(ANNOTATION_TAG_CALCULATED)));
|
||||
|
||||
if ((keep_price && details.price) ||
|
||||
(keep_date && details.date) ||
|
||||
(keep_tag && details.tag))
|
||||
{
|
||||
new_comm = parent().find_or_create
|
||||
(referent(), annotation_t(keep_price ? details.price : none,
|
||||
keep_date ? details.date : none,
|
||||
keep_tag ? details.tag : none));
|
||||
} else {
|
||||
new_comm = parent().find_or_create(base_symbol());
|
||||
}
|
||||
|
||||
assert(new_comm);
|
||||
return *new_comm;
|
||||
}
|
||||
|
||||
void annotated_commodity_t::write_annotations(std::ostream& out) const
|
||||
{
|
||||
details.print(out, parent().keep_base);
|
||||
}
|
||||
|
||||
void annotation_t::print(std::ostream& out, bool keep_base) const
|
||||
{
|
||||
if (price)
|
||||
out << " {"
|
||||
<< (has_flags(ANNOTATION_PRICE_FIXATED) ? "=" : "")
|
||||
<< (keep_base ? *price : price->unreduced()).rounded()
|
||||
<< '}';
|
||||
|
||||
if (date)
|
||||
out << " [" << format_date(*date, string("%Y/%m/%d")) << ']';
|
||||
|
||||
if (tag)
|
||||
out << " (" << *tag << ')';
|
||||
}
|
||||
|
||||
bool compare_amount_commodities::operator()(const amount_t * left,
|
||||
const amount_t * right) const
|
||||
{
|
||||
|
|
@ -972,199 +644,4 @@ bool compare_amount_commodities::operator()(const amount_t * left,
|
|||
}
|
||||
}
|
||||
|
||||
commodity_pool_t::commodity_pool_t()
|
||||
: default_commodity(NULL), keep_base(false)
|
||||
{
|
||||
TRACE_CTOR(commodity_pool_t, "");
|
||||
null_commodity = create("");
|
||||
null_commodity->add_flags(COMMODITY_BUILTIN | COMMODITY_NOMARKET);
|
||||
}
|
||||
|
||||
commodity_t * commodity_pool_t::create(const string& symbol)
|
||||
{
|
||||
shared_ptr<commodity_t::base_t>
|
||||
base_commodity(new commodity_t::base_t(symbol));
|
||||
std::auto_ptr<commodity_t> commodity(new commodity_t(this, base_commodity));
|
||||
|
||||
DEBUG("amounts.commodities", "Creating base commodity " << symbol);
|
||||
|
||||
// Create the "qualified symbol" version of this commodity's symbol
|
||||
if (commodity_t::symbol_needs_quotes(symbol)) {
|
||||
commodity->qualified_symbol = "\"";
|
||||
*commodity->qualified_symbol += symbol;
|
||||
*commodity->qualified_symbol += "\"";
|
||||
}
|
||||
|
||||
DEBUG("amounts.commodities",
|
||||
"Creating commodity '" << commodity->symbol() << "'");
|
||||
|
||||
std::pair<commodities_map::iterator, bool> result
|
||||
= commodities.insert(commodities_map::value_type(commodity->mapping_key(),
|
||||
commodity.get()));
|
||||
assert(result.second);
|
||||
|
||||
return commodity.release();
|
||||
}
|
||||
|
||||
commodity_t * commodity_pool_t::find_or_create(const string& symbol)
|
||||
{
|
||||
DEBUG("amounts.commodities", "Find-or-create commodity " << symbol);
|
||||
|
||||
commodity_t * commodity = find(symbol);
|
||||
if (commodity)
|
||||
return commodity;
|
||||
return create(symbol);
|
||||
}
|
||||
|
||||
commodity_t * commodity_pool_t::find(const string& symbol)
|
||||
{
|
||||
DEBUG("amounts.commodities", "Find commodity " << symbol);
|
||||
|
||||
commodities_map::const_iterator i = commodities.find(symbol);
|
||||
if (i != commodities.end())
|
||||
return (*i).second;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
commodity_t *
|
||||
commodity_pool_t::create(const string& symbol, const annotation_t& details)
|
||||
{
|
||||
commodity_t * new_comm = create(symbol);
|
||||
if (! new_comm)
|
||||
return NULL;
|
||||
|
||||
if (details)
|
||||
return find_or_create(*new_comm, details);
|
||||
else
|
||||
return new_comm;
|
||||
}
|
||||
|
||||
namespace {
|
||||
string make_qualified_name(const commodity_t& comm,
|
||||
const annotation_t& details)
|
||||
{
|
||||
assert(details);
|
||||
|
||||
if (details.price && details.price->sign() < 0)
|
||||
throw_(amount_error, _("A commodity's price may not be negative"));
|
||||
|
||||
std::ostringstream name;
|
||||
comm.print(name);
|
||||
details.print(name, comm.parent().keep_base);
|
||||
|
||||
DEBUG("amounts.commodities", "make_qualified_name for "
|
||||
<< *comm.qualified_symbol << std::endl << details);
|
||||
DEBUG("amounts.commodities", "qualified_name is " << name.str());
|
||||
|
||||
return name.str();
|
||||
}
|
||||
}
|
||||
|
||||
commodity_t *
|
||||
commodity_pool_t::find(const string& symbol, const annotation_t& details)
|
||||
{
|
||||
commodity_t * comm = find(symbol);
|
||||
if (! comm)
|
||||
return NULL;
|
||||
|
||||
if (details) {
|
||||
string name = make_qualified_name(*comm, details);
|
||||
|
||||
if (commodity_t * ann_comm = find(name)) {
|
||||
assert(ann_comm->annotated && as_annotated_commodity(*ann_comm).details);
|
||||
return ann_comm;
|
||||
}
|
||||
return NULL;
|
||||
} else {
|
||||
return comm;
|
||||
}
|
||||
}
|
||||
|
||||
commodity_t *
|
||||
commodity_pool_t::find_or_create(const string& symbol,
|
||||
const annotation_t& details)
|
||||
{
|
||||
commodity_t * comm = find(symbol);
|
||||
if (! comm)
|
||||
return NULL;
|
||||
|
||||
if (details)
|
||||
return find_or_create(*comm, details);
|
||||
else
|
||||
return comm;
|
||||
}
|
||||
|
||||
commodity_t *
|
||||
commodity_pool_t::create(commodity_t& comm,
|
||||
const annotation_t& details,
|
||||
const string& mapping_key)
|
||||
{
|
||||
assert(comm);
|
||||
assert(details);
|
||||
assert(! mapping_key.empty());
|
||||
|
||||
std::auto_ptr<commodity_t> commodity
|
||||
(new annotated_commodity_t(&comm, details));
|
||||
|
||||
commodity->qualified_symbol = comm.symbol();
|
||||
assert(! commodity->qualified_symbol->empty());
|
||||
|
||||
DEBUG("amounts.commodities", "Creating annotated commodity "
|
||||
<< "symbol " << commodity->symbol()
|
||||
<< " key " << mapping_key << std::endl << details);
|
||||
|
||||
// Add the fully annotated name to the map, so that this symbol may
|
||||
// quickly be found again.
|
||||
commodity->mapping_key_ = mapping_key;
|
||||
|
||||
std::pair<commodities_map::iterator, bool> result
|
||||
= commodities.insert(commodities_map::value_type(mapping_key,
|
||||
commodity.get()));
|
||||
assert(result.second);
|
||||
|
||||
return commodity.release();
|
||||
}
|
||||
|
||||
commodity_t * commodity_pool_t::find_or_create(commodity_t& comm,
|
||||
const annotation_t& details)
|
||||
{
|
||||
assert(comm);
|
||||
assert(details);
|
||||
|
||||
string name = make_qualified_name(comm, details);
|
||||
assert(! name.empty());
|
||||
|
||||
if (commodity_t * ann_comm = find(name)) {
|
||||
assert(ann_comm->annotated && as_annotated_commodity(*ann_comm).details);
|
||||
return ann_comm;
|
||||
}
|
||||
return create(comm, details, name);
|
||||
}
|
||||
|
||||
commodity_t *
|
||||
commodity_pool_t::parse_commodity_prices(const std::string& str,
|
||||
const bool add_prices,
|
||||
const optional<datetime_t>& moment)
|
||||
{
|
||||
scoped_array<char> buf(new char[str.length() + 1]);
|
||||
|
||||
std::strcpy(buf.get(), str.c_str());
|
||||
|
||||
char * price = std::strchr(buf.get(), '=');
|
||||
if (price)
|
||||
*price++ = '\0';
|
||||
|
||||
if (commodity_t * commodity = find_or_create(trim_ws(buf.get()))) {
|
||||
if (price && add_prices) {
|
||||
for (char * p = std::strtok(price, ";");
|
||||
p;
|
||||
p = std::strtok(NULL, ";")) {
|
||||
commodity->add_price(moment ? *moment : CURRENT_TIME(), amount_t(p));
|
||||
}
|
||||
}
|
||||
return commodity;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
} // namespace ledger
|
||||
|
|
|
|||
239
src/commodity.h
239
src/commodity.h
|
|
@ -309,37 +309,9 @@ public:
|
|||
return none;
|
||||
}
|
||||
|
||||
// Methods to exchange one commodity for another, while recording the
|
||||
// factored price.
|
||||
|
||||
static void exchange(commodity_t& commodity,
|
||||
const amount_t& per_unit_cost,
|
||||
const datetime_t& moment);
|
||||
|
||||
struct cost_breakdown_t {
|
||||
amount_t amount;
|
||||
amount_t final_cost;
|
||||
amount_t basis_cost;
|
||||
};
|
||||
|
||||
static cost_breakdown_t exchange(const amount_t& amount,
|
||||
const amount_t& cost,
|
||||
const bool is_per_unit = false,
|
||||
const optional<datetime_t>& moment = none,
|
||||
const optional<string>& tag = none);
|
||||
|
||||
// Methods related to parsing, reading, writing, etc., the commodity
|
||||
// itself.
|
||||
|
||||
static optional<path> price_db;
|
||||
static long download_leeway;
|
||||
static bool download_quotes;
|
||||
|
||||
static optional<price_point_t> parse_commodity_price(char * line);
|
||||
|
||||
optional<price_point_t>
|
||||
download_quote(const optional<commodity_t&>& commodity = none) const;
|
||||
|
||||
static void parse_symbol(std::istream& in, string& symbol);
|
||||
static void parse_symbol(char *& p, string& symbol);
|
||||
static string parse_symbol(std::istream& in) {
|
||||
|
|
@ -360,158 +332,6 @@ inline std::ostream& operator<<(std::ostream& out, const commodity_t& comm) {
|
|||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Brief
|
||||
*
|
||||
* Long.
|
||||
*/
|
||||
struct annotation_t : public supports_flags<>,
|
||||
public equality_comparable<annotation_t>
|
||||
{
|
||||
#define ANNOTATION_PRICE_CALCULATED 0x01
|
||||
#define ANNOTATION_PRICE_FIXATED 0x02
|
||||
#define ANNOTATION_DATE_CALCULATED 0x04
|
||||
#define ANNOTATION_TAG_CALCULATED 0x08
|
||||
|
||||
optional<amount_t> price;
|
||||
optional<date_t> date;
|
||||
optional<string> tag;
|
||||
|
||||
explicit annotation_t(const optional<amount_t>& _price = none,
|
||||
const optional<date_t>& _date = none,
|
||||
const optional<string>& _tag = none)
|
||||
: supports_flags<>(), price(_price), date(_date), tag(_tag) {
|
||||
TRACE_CTOR(annotation_t, "const optional<amount_t>& + date_t + string");
|
||||
}
|
||||
annotation_t(const annotation_t& other)
|
||||
: supports_flags<>(other.flags()),
|
||||
price(other.price), date(other.date), tag(other.tag) {
|
||||
TRACE_CTOR(annotation_t, "copy");
|
||||
}
|
||||
~annotation_t() {
|
||||
TRACE_DTOR(annotation_t);
|
||||
}
|
||||
|
||||
operator bool() const {
|
||||
return price || date || tag;
|
||||
}
|
||||
|
||||
bool operator==(const annotation_t& rhs) const {
|
||||
return (price == rhs.price &&
|
||||
date == rhs.date &&
|
||||
tag == rhs.tag);
|
||||
}
|
||||
|
||||
void parse(std::istream& in);
|
||||
|
||||
void print(std::ostream& out, bool keep_base = false) const;
|
||||
|
||||
bool valid() const {
|
||||
assert(*this);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct keep_details_t
|
||||
{
|
||||
bool keep_price;
|
||||
bool keep_date;
|
||||
bool keep_tag;
|
||||
bool only_actuals;
|
||||
|
||||
explicit keep_details_t(bool _keep_price = false,
|
||||
bool _keep_date = false,
|
||||
bool _keep_tag = false,
|
||||
bool _only_actuals = false)
|
||||
: keep_price(_keep_price),
|
||||
keep_date(_keep_date),
|
||||
keep_tag(_keep_tag),
|
||||
only_actuals(_only_actuals)
|
||||
{
|
||||
TRACE_CTOR(keep_details_t, "bool, bool, bool, bool");
|
||||
}
|
||||
keep_details_t(const keep_details_t& other)
|
||||
: keep_price(other.keep_price), keep_date(other.keep_date),
|
||||
keep_tag(other.keep_tag), only_actuals(other.only_actuals) {
|
||||
TRACE_CTOR(keep_details_t, "copy");
|
||||
}
|
||||
~keep_details_t() throw() {
|
||||
TRACE_DTOR(keep_details_t);
|
||||
}
|
||||
|
||||
bool keep_all() const {
|
||||
return keep_price && keep_date && keep_tag && ! only_actuals;
|
||||
}
|
||||
bool keep_all(const commodity_t& comm) const {
|
||||
return (! comm.annotated ||
|
||||
(keep_price && keep_date && keep_tag && ! only_actuals));
|
||||
}
|
||||
|
||||
bool keep_any() const {
|
||||
return keep_price || keep_date || keep_tag;
|
||||
}
|
||||
bool keep_any(const commodity_t& comm) const {
|
||||
return comm.annotated && (keep_price || keep_date || keep_tag);
|
||||
}
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& out,
|
||||
const annotation_t& details) {
|
||||
details.print(out);
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Brief
|
||||
*
|
||||
* Long.
|
||||
*/
|
||||
class annotated_commodity_t
|
||||
: public commodity_t,
|
||||
public equality_comparable<annotated_commodity_t,
|
||||
equality_comparable2<annotated_commodity_t, commodity_t,
|
||||
noncopyable> >
|
||||
{
|
||||
public:
|
||||
commodity_t * ptr;
|
||||
annotation_t details;
|
||||
|
||||
explicit annotated_commodity_t(commodity_t * _ptr,
|
||||
const annotation_t& _details)
|
||||
: commodity_t(_ptr->parent_, _ptr->base), ptr(_ptr), details(_details) {
|
||||
TRACE_CTOR(annotated_commodity_t, "");
|
||||
annotated = true;
|
||||
}
|
||||
virtual ~annotated_commodity_t() {
|
||||
TRACE_DTOR(annotated_commodity_t);
|
||||
}
|
||||
|
||||
virtual bool operator==(const commodity_t& comm) const;
|
||||
virtual bool operator==(const annotated_commodity_t& comm) const {
|
||||
return *this == static_cast<const commodity_t&>(comm);
|
||||
}
|
||||
|
||||
virtual commodity_t& referent() {
|
||||
return *ptr;
|
||||
}
|
||||
virtual const commodity_t& referent() const {
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
virtual commodity_t& strip_annotations(const keep_details_t& what_to_keep);
|
||||
virtual void write_annotations(std::ostream& out) const;
|
||||
};
|
||||
|
||||
inline annotated_commodity_t&
|
||||
as_annotated_commodity(commodity_t& commodity) {
|
||||
return downcast<annotated_commodity_t>(commodity);
|
||||
}
|
||||
inline const annotated_commodity_t&
|
||||
as_annotated_commodity(const commodity_t& commodity) {
|
||||
return downcast<const annotated_commodity_t>(commodity);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Brief
|
||||
*
|
||||
|
|
@ -521,65 +341,6 @@ struct compare_amount_commodities {
|
|||
bool operator()(const amount_t * left, const amount_t * right) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Brief
|
||||
*
|
||||
* Long.
|
||||
*/
|
||||
class commodity_pool_t : public noncopyable
|
||||
{
|
||||
/**
|
||||
* The commodities collection in commodity_pool_t maintains pointers to all
|
||||
* the commodities which have ever been created by the user, whether
|
||||
* explicitly by calling the create methods of commodity_pool_t, or
|
||||
* implicitly by parsing a commoditized amount.
|
||||
*/
|
||||
typedef std::map<string, commodity_t *> commodities_map;
|
||||
|
||||
public:
|
||||
commodities_map commodities;
|
||||
|
||||
commodity_t * null_commodity;
|
||||
commodity_t * default_commodity;
|
||||
|
||||
bool keep_base;
|
||||
|
||||
public:
|
||||
boost::function<optional<amount_t>
|
||||
(commodity_t& commodity,
|
||||
const optional<datetime_t>& date,
|
||||
const optional<datetime_t>& moment,
|
||||
const optional<datetime_t>& last)> get_quote;
|
||||
|
||||
explicit commodity_pool_t();
|
||||
|
||||
~commodity_pool_t() {
|
||||
TRACE_DTOR(commodity_pool_t);
|
||||
foreach (commodities_map::value_type pair, commodities)
|
||||
checked_delete(pair.second);
|
||||
}
|
||||
|
||||
commodity_t * create(const string& symbol);
|
||||
commodity_t * find(const string& name);
|
||||
commodity_t * find_or_create(const string& symbol);
|
||||
|
||||
commodity_t * create(const string& symbol, const annotation_t& details);
|
||||
commodity_t * find(const string& symbol, const annotation_t& details);
|
||||
commodity_t * find_or_create(const string& symbol,
|
||||
const annotation_t& details);
|
||||
|
||||
commodity_t * create(commodity_t& comm,
|
||||
const annotation_t& details,
|
||||
const string& mapping_key);
|
||||
|
||||
commodity_t * find_or_create(commodity_t& comm,
|
||||
const annotation_t& details);
|
||||
|
||||
commodity_t * parse_commodity_prices(const std::string& str,
|
||||
const bool add_prices = true,
|
||||
const optional<datetime_t>& moment = none);
|
||||
};
|
||||
|
||||
} // namespace ledger
|
||||
|
||||
#endif // _COMMODITY_H
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@
|
|||
#endif
|
||||
#include "item.h"
|
||||
#include "journal.h"
|
||||
#include "pool.h"
|
||||
|
||||
namespace ledger {
|
||||
|
||||
|
|
@ -416,19 +417,18 @@ void global_scope_t::normalize_report_options(const string& verb)
|
|||
report_t& rep(report());
|
||||
|
||||
// jww (2009-02-09): These globals are a hack, but hard to avoid.
|
||||
item_t::use_effective_date = rep.HANDLED(effective);
|
||||
rep.session.commodity_pool->keep_base = rep.HANDLED(base);
|
||||
|
||||
commodity_t::download_quotes = rep.session.HANDLED(download);
|
||||
item_t::use_effective_date = rep.HANDLED(effective);
|
||||
rep.session.commodity_pool->keep_base = rep.HANDLED(base);
|
||||
rep.session.commodity_pool->download_quotes = rep.session.HANDLED(download);
|
||||
|
||||
if (rep.session.HANDLED(price_exp_))
|
||||
commodity_t::download_leeway =
|
||||
rep.session.commodity_pool->download_leeway =
|
||||
rep.session.HANDLER(price_exp_).value.as_long();
|
||||
|
||||
if (rep.session.HANDLED(price_db_))
|
||||
commodity_t::price_db = rep.session.HANDLER(price_db_).str();
|
||||
rep.session.commodity_pool->price_db = rep.session.HANDLER(price_db_).str();
|
||||
else
|
||||
commodity_t::price_db = none;
|
||||
rep.session.commodity_pool->price_db = none;
|
||||
|
||||
if (rep.HANDLED(date_format_)) {
|
||||
output_datetime_format = rep.HANDLER(date_format_).str() + " %H:%M:%S";
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
#include "op.h"
|
||||
#include "scope.h"
|
||||
#include "commodity.h"
|
||||
#include "pool.h"
|
||||
|
||||
namespace ledger {
|
||||
|
||||
|
|
|
|||
409
src/pool.cc
Normal file
409
src/pool.cc
Normal file
|
|
@ -0,0 +1,409 @@
|
|||
/*
|
||||
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of New Artisans LLC nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <system.hh>
|
||||
|
||||
#include "amount.h"
|
||||
#include "commodity.h"
|
||||
#include "annotate.h"
|
||||
#include "pool.h"
|
||||
|
||||
namespace ledger {
|
||||
|
||||
commodity_pool_t::commodity_pool_t()
|
||||
: default_commodity(NULL), keep_base(false),
|
||||
download_leeway(86400), download_quotes(false)
|
||||
{
|
||||
TRACE_CTOR(commodity_pool_t, "");
|
||||
null_commodity = create("");
|
||||
null_commodity->add_flags(COMMODITY_BUILTIN | COMMODITY_NOMARKET);
|
||||
}
|
||||
|
||||
commodity_t * commodity_pool_t::create(const string& symbol)
|
||||
{
|
||||
shared_ptr<commodity_t::base_t>
|
||||
base_commodity(new commodity_t::base_t(symbol));
|
||||
std::auto_ptr<commodity_t> commodity(new commodity_t(this, base_commodity));
|
||||
|
||||
DEBUG("amounts.commodities", "Creating base commodity " << symbol);
|
||||
|
||||
// Create the "qualified symbol" version of this commodity's symbol
|
||||
if (commodity_t::symbol_needs_quotes(symbol)) {
|
||||
commodity->qualified_symbol = "\"";
|
||||
*commodity->qualified_symbol += symbol;
|
||||
*commodity->qualified_symbol += "\"";
|
||||
}
|
||||
|
||||
DEBUG("amounts.commodities",
|
||||
"Creating commodity '" << commodity->symbol() << "'");
|
||||
|
||||
std::pair<commodities_map::iterator, bool> result
|
||||
= commodities.insert(commodities_map::value_type(commodity->mapping_key(),
|
||||
commodity.get()));
|
||||
assert(result.second);
|
||||
|
||||
return commodity.release();
|
||||
}
|
||||
|
||||
commodity_t * commodity_pool_t::find_or_create(const string& symbol)
|
||||
{
|
||||
DEBUG("amounts.commodities", "Find-or-create commodity " << symbol);
|
||||
|
||||
commodity_t * commodity = find(symbol);
|
||||
if (commodity)
|
||||
return commodity;
|
||||
return create(symbol);
|
||||
}
|
||||
|
||||
commodity_t * commodity_pool_t::find(const string& symbol)
|
||||
{
|
||||
DEBUG("amounts.commodities", "Find commodity " << symbol);
|
||||
|
||||
commodities_map::const_iterator i = commodities.find(symbol);
|
||||
if (i != commodities.end())
|
||||
return (*i).second;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
commodity_t *
|
||||
commodity_pool_t::create(const string& symbol, const annotation_t& details)
|
||||
{
|
||||
commodity_t * new_comm = create(symbol);
|
||||
if (! new_comm)
|
||||
return NULL;
|
||||
|
||||
if (details)
|
||||
return find_or_create(*new_comm, details);
|
||||
else
|
||||
return new_comm;
|
||||
}
|
||||
|
||||
namespace {
|
||||
string make_qualified_name(const commodity_t& comm,
|
||||
const annotation_t& details)
|
||||
{
|
||||
assert(details);
|
||||
|
||||
if (details.price && details.price->sign() < 0)
|
||||
throw_(amount_error, _("A commodity's price may not be negative"));
|
||||
|
||||
std::ostringstream name;
|
||||
comm.print(name);
|
||||
details.print(name, comm.parent().keep_base);
|
||||
|
||||
DEBUG("amounts.commodities", "make_qualified_name for "
|
||||
<< *comm.qualified_symbol << std::endl << details);
|
||||
DEBUG("amounts.commodities", "qualified_name is " << name.str());
|
||||
|
||||
return name.str();
|
||||
}
|
||||
}
|
||||
|
||||
commodity_t *
|
||||
commodity_pool_t::find(const string& symbol, const annotation_t& details)
|
||||
{
|
||||
commodity_t * comm = find(symbol);
|
||||
if (! comm)
|
||||
return NULL;
|
||||
|
||||
if (details) {
|
||||
string name = make_qualified_name(*comm, details);
|
||||
|
||||
if (commodity_t * ann_comm = find(name)) {
|
||||
assert(ann_comm->annotated && as_annotated_commodity(*ann_comm).details);
|
||||
return ann_comm;
|
||||
}
|
||||
return NULL;
|
||||
} else {
|
||||
return comm;
|
||||
}
|
||||
}
|
||||
|
||||
commodity_t *
|
||||
commodity_pool_t::find_or_create(const string& symbol,
|
||||
const annotation_t& details)
|
||||
{
|
||||
commodity_t * comm = find(symbol);
|
||||
if (! comm)
|
||||
return NULL;
|
||||
|
||||
if (details)
|
||||
return find_or_create(*comm, details);
|
||||
else
|
||||
return comm;
|
||||
}
|
||||
|
||||
commodity_t *
|
||||
commodity_pool_t::create(commodity_t& comm,
|
||||
const annotation_t& details,
|
||||
const string& mapping_key)
|
||||
{
|
||||
assert(comm);
|
||||
assert(details);
|
||||
assert(! mapping_key.empty());
|
||||
|
||||
std::auto_ptr<commodity_t> commodity
|
||||
(new annotated_commodity_t(&comm, details));
|
||||
|
||||
commodity->qualified_symbol = comm.symbol();
|
||||
assert(! commodity->qualified_symbol->empty());
|
||||
|
||||
DEBUG("amounts.commodities", "Creating annotated commodity "
|
||||
<< "symbol " << commodity->symbol()
|
||||
<< " key " << mapping_key << std::endl << details);
|
||||
|
||||
// Add the fully annotated name to the map, so that this symbol may
|
||||
// quickly be found again.
|
||||
commodity->mapping_key_ = mapping_key;
|
||||
|
||||
std::pair<commodities_map::iterator, bool> result
|
||||
= commodities.insert(commodities_map::value_type(mapping_key,
|
||||
commodity.get()));
|
||||
assert(result.second);
|
||||
|
||||
return commodity.release();
|
||||
}
|
||||
|
||||
commodity_t * commodity_pool_t::find_or_create(commodity_t& comm,
|
||||
const annotation_t& details)
|
||||
{
|
||||
assert(comm);
|
||||
assert(details);
|
||||
|
||||
string name = make_qualified_name(comm, details);
|
||||
assert(! name.empty());
|
||||
|
||||
if (commodity_t * ann_comm = find(name)) {
|
||||
assert(ann_comm->annotated && as_annotated_commodity(*ann_comm).details);
|
||||
return ann_comm;
|
||||
}
|
||||
return create(comm, details, name);
|
||||
}
|
||||
|
||||
optional<price_point_t> commodity_pool_t::parse_price_directive(char * line)
|
||||
{
|
||||
char * date_field_ptr = line;
|
||||
char * time_field_ptr = next_element(date_field_ptr);
|
||||
if (! time_field_ptr) return none;
|
||||
string date_field = date_field_ptr;
|
||||
|
||||
char * symbol_and_price;
|
||||
datetime_t datetime;
|
||||
|
||||
if (std::isdigit(time_field_ptr[0])) {
|
||||
symbol_and_price = next_element(time_field_ptr);
|
||||
if (! symbol_and_price) return none;
|
||||
datetime = parse_datetime(date_field + " " + time_field_ptr);
|
||||
} else {
|
||||
symbol_and_price = time_field_ptr;
|
||||
datetime = parse_datetime(date_field);
|
||||
}
|
||||
|
||||
string symbol;
|
||||
commodity_t::parse_symbol(symbol_and_price, symbol);
|
||||
|
||||
price_point_t point;
|
||||
point.when = datetime;
|
||||
point.price.parse(symbol_and_price);
|
||||
VERIFY(point.price.valid());
|
||||
|
||||
if (commodity_t * commodity =
|
||||
amount_t::current_pool->find_or_create(symbol)) {
|
||||
commodity->add_price(point.when, point.price, true);
|
||||
commodity->add_flags(COMMODITY_KNOWN);
|
||||
return point;
|
||||
}
|
||||
|
||||
return none;
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
optional<price_point_t>
|
||||
commodity_t::download_quote(const optional<commodity_t&>& commodity) const
|
||||
{
|
||||
DEBUG("commodity.download", "downloading quote for symbol " << symbol());
|
||||
#if defined(DEBUG_ON)
|
||||
if (commodity)
|
||||
DEBUG("commodity.download",
|
||||
" in terms of commodity " << commodity->symbol());
|
||||
#endif
|
||||
|
||||
char buf[256];
|
||||
buf[0] = '\0';
|
||||
|
||||
string getquote_cmd("getquote \"");
|
||||
getquote_cmd += symbol();
|
||||
getquote_cmd += "\" \"";
|
||||
if (commodity)
|
||||
getquote_cmd += commodity->symbol();
|
||||
getquote_cmd += "\"";
|
||||
|
||||
DEBUG("commodity.download", "invoking command: " << getquote_cmd);
|
||||
|
||||
bool success = true;
|
||||
if (FILE * fp = popen(getquote_cmd.c_str(), "r")) {
|
||||
if (std::feof(fp) || ! std::fgets(buf, 255, fp))
|
||||
success = false;
|
||||
if (pclose(fp) != 0)
|
||||
success = false;
|
||||
} else {
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (success && buf[0]) {
|
||||
if (char * p = std::strchr(buf, '\n')) *p = '\0';
|
||||
DEBUG("commodity.download", "downloaded quote: " << buf);
|
||||
|
||||
if (optional<price_point_t> point = parse_commodity_price(buf)) {
|
||||
if (price_db) {
|
||||
#if defined(__GNUG__) && __GNUG__ < 3
|
||||
ofstream database(*price_db, ios::out | ios::app);
|
||||
#else
|
||||
ofstream database(*price_db, std::ios_base::out | std::ios_base::app);
|
||||
#endif
|
||||
database << "P " << format_datetime(point->when, string("%Y/%m/%d %H:%M:%S"))
|
||||
<< " " << symbol() << " " << point->price << std::endl;
|
||||
}
|
||||
return point;
|
||||
}
|
||||
} else {
|
||||
throw_(std::runtime_error,
|
||||
_("Failed to download price for '%1' (command: \"getquote %2 %3\")")
|
||||
<< symbol() << symbol() << (commodity ? commodity->symbol() : "''"));
|
||||
}
|
||||
return none;
|
||||
}
|
||||
#endif
|
||||
|
||||
void commodity_pool_t::exchange(commodity_t& commodity,
|
||||
const amount_t& per_unit_cost,
|
||||
const datetime_t& moment)
|
||||
{
|
||||
DEBUG("commodity.prices.add", "exchanging commodity " << commodity
|
||||
<< " at per unit cost " << per_unit_cost << " on " << moment);
|
||||
|
||||
commodity_t& base_commodity
|
||||
(commodity.annotated ?
|
||||
as_annotated_commodity(commodity).referent() : commodity);
|
||||
|
||||
base_commodity.add_price(moment, per_unit_cost);
|
||||
}
|
||||
|
||||
cost_breakdown_t
|
||||
commodity_pool_t::exchange(const amount_t& amount,
|
||||
const amount_t& cost,
|
||||
const bool is_per_unit,
|
||||
const optional<datetime_t>& moment,
|
||||
const optional<string>& tag)
|
||||
{
|
||||
DEBUG("commodity.prices.add", "exchange: " << amount << " for " << cost);
|
||||
DEBUG("commodity.prices.add", "exchange: is-per-unit = " << is_per_unit);
|
||||
#if defined(DEBUG_ON)
|
||||
if (moment)
|
||||
DEBUG("commodity.prices.add", "exchange: moment = " << *moment);
|
||||
if (tag)
|
||||
DEBUG("commodity.prices.add", "exchange: tag = " << *tag);
|
||||
#endif
|
||||
|
||||
commodity_t& commodity(amount.commodity());
|
||||
|
||||
annotation_t * current_annotation = NULL;
|
||||
if (commodity.annotated)
|
||||
current_annotation = &as_annotated_commodity(commodity).details;
|
||||
|
||||
amount_t per_unit_cost =
|
||||
(is_per_unit || amount.is_realzero() ? cost : cost / amount).abs();
|
||||
|
||||
DEBUG("commodity.prices.add", "exchange: per-unit-cost = " << per_unit_cost);
|
||||
|
||||
if (! per_unit_cost.is_realzero())
|
||||
exchange(commodity, per_unit_cost, moment ? *moment : CURRENT_TIME());
|
||||
|
||||
cost_breakdown_t breakdown;
|
||||
breakdown.final_cost = ! is_per_unit ? cost : cost * amount;
|
||||
|
||||
DEBUG("commodity.prices.add",
|
||||
"exchange: final-cost = " << breakdown.final_cost);
|
||||
|
||||
if (current_annotation && current_annotation->price)
|
||||
breakdown.basis_cost
|
||||
= (*current_annotation->price * amount).unrounded();
|
||||
else
|
||||
breakdown.basis_cost = breakdown.final_cost;
|
||||
|
||||
DEBUG("commodity.prices.add",
|
||||
"exchange: basis-cost = " << breakdown.basis_cost);
|
||||
|
||||
annotation_t annotation(per_unit_cost, moment ?
|
||||
moment->date() : optional<date_t>(), tag);
|
||||
|
||||
annotation.add_flags(ANNOTATION_PRICE_CALCULATED);
|
||||
if (moment)
|
||||
annotation.add_flags(ANNOTATION_DATE_CALCULATED);
|
||||
if (tag)
|
||||
annotation.add_flags(ANNOTATION_TAG_CALCULATED);
|
||||
|
||||
breakdown.amount = amount_t(amount, annotation);
|
||||
|
||||
DEBUG("commodity.prices.add",
|
||||
"exchange: amount = " << breakdown.amount);
|
||||
|
||||
return breakdown;
|
||||
}
|
||||
|
||||
commodity_t *
|
||||
commodity_pool_t::parse_price_expression(const std::string& str,
|
||||
const bool add_prices,
|
||||
const optional<datetime_t>& moment)
|
||||
{
|
||||
scoped_array<char> buf(new char[str.length() + 1]);
|
||||
|
||||
std::strcpy(buf.get(), str.c_str());
|
||||
|
||||
char * price = std::strchr(buf.get(), '=');
|
||||
if (price)
|
||||
*price++ = '\0';
|
||||
|
||||
if (commodity_t * commodity = find_or_create(trim_ws(buf.get()))) {
|
||||
if (price && add_prices) {
|
||||
for (char * p = std::strtok(price, ";");
|
||||
p;
|
||||
p = std::strtok(NULL, ";")) {
|
||||
commodity->add_price(moment ? *moment : CURRENT_TIME(), amount_t(p));
|
||||
}
|
||||
}
|
||||
return commodity;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
} // namespace ledger
|
||||
141
src/pool.h
Normal file
141
src/pool.h
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of New Artisans LLC nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup math
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file pool.h
|
||||
* @author John Wiegley
|
||||
*
|
||||
* @ingroup math
|
||||
*
|
||||
* @brief Types for managing commodity pools
|
||||
*
|
||||
* Long.
|
||||
*/
|
||||
#ifndef _POOL_H
|
||||
#define _POOL_H
|
||||
|
||||
namespace ledger {
|
||||
|
||||
/**
|
||||
* @brief Brief
|
||||
*
|
||||
* Long.
|
||||
*/
|
||||
struct cost_breakdown_t
|
||||
{
|
||||
amount_t amount;
|
||||
amount_t final_cost;
|
||||
amount_t basis_cost;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Brief
|
||||
*
|
||||
* Long.
|
||||
*/
|
||||
class commodity_pool_t : public noncopyable
|
||||
{
|
||||
/**
|
||||
* The commodities collection in commodity_pool_t maintains pointers to all
|
||||
* the commodities which have ever been created by the user, whether
|
||||
* explicitly by calling the create methods of commodity_pool_t, or
|
||||
* implicitly by parsing a commoditized amount.
|
||||
*/
|
||||
typedef std::map<string, commodity_t *> commodities_map;
|
||||
|
||||
public:
|
||||
commodities_map commodities;
|
||||
commodity_t * null_commodity;
|
||||
commodity_t * default_commodity;
|
||||
|
||||
bool keep_base; // --base
|
||||
|
||||
optional<path> price_db; // --price-db=
|
||||
long download_leeway; // --leeway=
|
||||
bool download_quotes; // --download
|
||||
|
||||
public:
|
||||
function<optional<price_point_t>
|
||||
(const optional<commodity_t&>& commodity)> get_commodity_quote;
|
||||
|
||||
explicit commodity_pool_t();
|
||||
|
||||
~commodity_pool_t() {
|
||||
TRACE_DTOR(commodity_pool_t);
|
||||
foreach (commodities_map::value_type pair, commodities)
|
||||
checked_delete(pair.second);
|
||||
}
|
||||
|
||||
commodity_t * create(const string& symbol);
|
||||
commodity_t * find(const string& name);
|
||||
commodity_t * find_or_create(const string& symbol);
|
||||
|
||||
commodity_t * create(const string& symbol, const annotation_t& details);
|
||||
commodity_t * find(const string& symbol, const annotation_t& details);
|
||||
commodity_t * find_or_create(const string& symbol,
|
||||
const annotation_t& details);
|
||||
|
||||
commodity_t * create(commodity_t& comm,
|
||||
const annotation_t& details,
|
||||
const string& mapping_key);
|
||||
|
||||
commodity_t * find_or_create(commodity_t& comm,
|
||||
const annotation_t& details);
|
||||
|
||||
// Exchange one commodity for another, while recording the factored price.
|
||||
|
||||
void exchange(commodity_t& commodity,
|
||||
const amount_t& per_unit_cost,
|
||||
const datetime_t& moment);
|
||||
|
||||
cost_breakdown_t exchange(const amount_t& amount,
|
||||
const amount_t& cost,
|
||||
const bool is_per_unit = false,
|
||||
const optional<datetime_t>& moment = none,
|
||||
const optional<string>& tag = none);
|
||||
|
||||
// Parse commodity prices from a textual representation
|
||||
|
||||
optional<price_point_t> parse_price_directive(char * line);
|
||||
|
||||
commodity_t *
|
||||
parse_price_expression(const std::string& str,
|
||||
const bool add_prices = true,
|
||||
const optional<datetime_t>& moment = none);
|
||||
};
|
||||
|
||||
} // namespace ledger
|
||||
|
||||
#endif // _POOL_H
|
||||
|
|
@ -48,6 +48,7 @@
|
|||
|
||||
#include "expr.h"
|
||||
#include "commodity.h"
|
||||
#include "annotate.h"
|
||||
|
||||
namespace ledger {
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@
|
|||
#include "pyutils.h"
|
||||
#include "pyfstream.h"
|
||||
#include "commodity.h"
|
||||
#include "annotate.h"
|
||||
#include "pool.h"
|
||||
|
||||
namespace ledger {
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
#include "pyinterp.h"
|
||||
#include "pyutils.h"
|
||||
#include "commodity.h"
|
||||
#include "annotate.h"
|
||||
|
||||
namespace ledger {
|
||||
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@
|
|||
#include "stream.h"
|
||||
#include "option.h"
|
||||
#include "commodity.h"
|
||||
#include "annotate.h"
|
||||
#include "format.h"
|
||||
|
||||
namespace ledger {
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
|
||||
#include "session.h"
|
||||
#include "commodity.h"
|
||||
#include "pool.h"
|
||||
#include "xact.h"
|
||||
#include "account.h"
|
||||
#include "journal.h"
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
#include "account.h"
|
||||
#include "option.h"
|
||||
#include "pstream.h"
|
||||
#include "pool.h"
|
||||
|
||||
#define TIMELOG_SUPPORT 1
|
||||
#if defined(TIMELOG_SUPPORT)
|
||||
|
|
@ -458,7 +459,7 @@ void instance_t::price_conversion_directive(char * line)
|
|||
void instance_t::price_xact_directive(char * line)
|
||||
{
|
||||
optional<price_point_t> point =
|
||||
commodity_t::parse_commodity_price(skip_ws(line + 1));
|
||||
amount_t::current_pool->parse_price_directive(skip_ws(line + 1));
|
||||
assert(point);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@
|
|||
|
||||
#include "value.h"
|
||||
#include "commodity.h"
|
||||
#include "annotate.h"
|
||||
#include "pool.h"
|
||||
#include "unistring.h"
|
||||
|
||||
namespace ledger {
|
||||
|
|
@ -1240,7 +1242,7 @@ value_t value_t::exchange_commodities(const std::string& commodities,
|
|||
p;
|
||||
p = std::strtok(NULL, ",")) {
|
||||
if (commodity_t * commodity =
|
||||
amount_t::current_pool->parse_commodity_prices(p, add_prices, moment)) {
|
||||
amount_t::current_pool->parse_price_expression(p, add_prices, moment)) {
|
||||
value_t result = value(false, moment, *commodity);
|
||||
if (! result.is_null())
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@
|
|||
#include "post.h"
|
||||
#include "account.h"
|
||||
#include "journal.h"
|
||||
#include "pool.h"
|
||||
|
||||
namespace ledger {
|
||||
|
||||
|
|
@ -269,9 +270,9 @@ bool xact_base_t::finalize()
|
|||
throw_(balance_error,
|
||||
_("A posting's cost must be of a different commodity than its amount"));
|
||||
|
||||
commodity_t::cost_breakdown_t breakdown =
|
||||
commodity_t::exchange(post->amount, *post->cost, false,
|
||||
datetime_t(date(), time_duration(0, 0, 0, 0)));
|
||||
cost_breakdown_t breakdown =
|
||||
amount_t::current_pool->exchange(post->amount, *post->cost, false,
|
||||
datetime_t(date(), time_duration(0, 0, 0, 0)));
|
||||
|
||||
if (post->amount.is_annotated() &&
|
||||
breakdown.basis_cost.commodity() ==
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue