Cleaned up the way that commodity pricing is handled.
This commit is contained in:
parent
0e95974360
commit
1bbb6933af
5 changed files with 64 additions and 153 deletions
|
|
@ -241,6 +241,12 @@ public:
|
||||||
else
|
else
|
||||||
commodity_ = NULL;
|
commodity_ = NULL;
|
||||||
}
|
}
|
||||||
|
amount_t(const amount_t& amt, const annotation_t& details) : quantity(NULL) {
|
||||||
|
TRACE_CTOR(amount_t, "const amount_t&, const annotation_t&");
|
||||||
|
assert(amt.quantity);
|
||||||
|
_copy(amt);
|
||||||
|
annotate(details);
|
||||||
|
}
|
||||||
amount_t& operator=(const amount_t& amt);
|
amount_t& operator=(const amount_t& amt);
|
||||||
|
|
||||||
#ifdef HAVE_GDTOA
|
#ifdef HAVE_GDTOA
|
||||||
|
|
|
||||||
|
|
@ -43,8 +43,7 @@
|
||||||
|
|
||||||
namespace ledger {
|
namespace ledger {
|
||||||
|
|
||||||
void commodity_t::add_price(const datetime_t& date,
|
void commodity_t::add_price(const datetime_t& date, const amount_t& price)
|
||||||
const amount_t& price)
|
|
||||||
{
|
{
|
||||||
if (! base->history)
|
if (! base->history)
|
||||||
base->history = history_t();
|
base->history = history_t();
|
||||||
|
|
@ -117,19 +116,26 @@ optional<amount_t> commodity_t::value(const optional<datetime_t>& moment)
|
||||||
return price;
|
return price;
|
||||||
}
|
}
|
||||||
|
|
||||||
amount_t commodity_t::exchange(const amount_t& amount,
|
void commodity_t::exchange(commodity_t& commodity,
|
||||||
amount_t& final_cost, // out
|
const amount_t& per_unit_cost,
|
||||||
amount_t& basis_cost, // out
|
const datetime_t& moment)
|
||||||
const optional<amount_t>& total_cost_,
|
|
||||||
const optional<amount_t>& per_unit_cost_,
|
|
||||||
const optional<datetime_t>& moment,
|
|
||||||
const optional<string>& tag)
|
|
||||||
{
|
{
|
||||||
// (assert (or (and total-cost (not per-unit-cost))
|
if (! commodity.has_flags(COMMODITY_STYLE_NOMARKET)) {
|
||||||
// (and per-unit-cost (not total-cost))))
|
commodity_t& base_commodity
|
||||||
|
(commodity.annotated ?
|
||||||
|
as_annotated_commodity(commodity).referent() : commodity);
|
||||||
|
|
||||||
assert((total_cost_ && ! per_unit_cost_) || (per_unit_cost_ && ! total_cost_));
|
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)
|
||||||
|
{
|
||||||
// (let* ((commodity (amount-commodity amount))
|
// (let* ((commodity (amount-commodity amount))
|
||||||
// (current-annotation
|
// (current-annotation
|
||||||
// (and (annotated-commodity-p commodity)
|
// (and (annotated-commodity-p commodity)
|
||||||
|
|
@ -152,9 +158,10 @@ amount_t commodity_t::exchange(const amount_t& amount,
|
||||||
(current_annotation ?
|
(current_annotation ?
|
||||||
as_annotated_commodity(commodity).referent() : commodity);
|
as_annotated_commodity(commodity).referent() : commodity);
|
||||||
|
|
||||||
amount_t per_unit_cost(per_unit_cost_ ?
|
amount_t per_unit_cost(is_per_unit ? cost : cost / amount);
|
||||||
*per_unit_cost_ : *total_cost_ / amount);
|
|
||||||
final_cost = total_cost_ ? *total_cost_ : *per_unit_cost_ * amount;
|
cost_breakdown_t breakdown;
|
||||||
|
breakdown.final_cost = ! is_per_unit ? cost : cost * amount;
|
||||||
|
|
||||||
// Add a price history entry for this conversion if we know when it took
|
// Add a price history entry for this conversion if we know when it took
|
||||||
// place
|
// place
|
||||||
|
|
@ -177,16 +184,15 @@ amount_t commodity_t::exchange(const amount_t& amount,
|
||||||
// total-cost))))
|
// total-cost))))
|
||||||
|
|
||||||
if (current_annotation && current_annotation->price)
|
if (current_annotation && current_annotation->price)
|
||||||
basis_cost = *current_annotation->price * amount;
|
breakdown.basis_cost = *current_annotation->price * amount;
|
||||||
else
|
else
|
||||||
basis_cost = final_cost;
|
breakdown.basis_cost = breakdown.final_cost;
|
||||||
|
|
||||||
amount_t ann_amount(amount);
|
breakdown.amount =
|
||||||
ann_amount.annotate
|
amount_t(amount, annotation_t (per_unit_cost, moment ?
|
||||||
(annotation_t(per_unit_cost,
|
moment->date() : optional<date_t>(), tag));
|
||||||
moment ? moment->date() : optional<date_t>(), tag));
|
|
||||||
|
|
||||||
return ann_amount;
|
return breakdown;
|
||||||
}
|
}
|
||||||
|
|
||||||
commodity_t::operator bool() const
|
commodity_t::operator bool() const
|
||||||
|
|
|
||||||
|
|
@ -192,18 +192,26 @@ public:
|
||||||
return base->history;
|
return base->history;
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_price(const datetime_t& date, const amount_t& price);
|
void add_price(const datetime_t& date, const amount_t& price);
|
||||||
bool remove_price(const datetime_t& date);
|
bool remove_price(const datetime_t& date);
|
||||||
|
|
||||||
optional<amount_t> value(const optional<datetime_t>& moment = none);
|
optional<amount_t> value(const optional<datetime_t>& moment = none);
|
||||||
|
|
||||||
static amount_t exchange(const amount_t& amount,
|
struct cost_breakdown_t {
|
||||||
amount_t& final_cost, // out
|
amount_t amount;
|
||||||
amount_t& basis_cost, // out
|
amount_t final_cost;
|
||||||
const optional<amount_t>& total_cost,
|
amount_t basis_cost;
|
||||||
const optional<amount_t>& per_unit_cost = none,
|
};
|
||||||
const optional<datetime_t>& moment = none,
|
|
||||||
const optional<string>& tag = none);
|
static void exchange(commodity_t& commodity,
|
||||||
|
const amount_t& per_unit_cost,
|
||||||
|
const datetime_t& moment);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
static void parse_symbol(std::istream& in, string& symbol);
|
static void parse_symbol(std::istream& in, string& symbol);
|
||||||
static void parse_symbol(char *& p, string& symbol);
|
static void parse_symbol(char *& p, string& symbol);
|
||||||
|
|
|
||||||
127
src/entry.cc
127
src/entry.cc
|
|
@ -98,19 +98,6 @@ bool entry_base_t::finalize()
|
||||||
value_t balance;
|
value_t balance;
|
||||||
xact_t * null_xact = NULL;
|
xact_t * null_xact = NULL;
|
||||||
|
|
||||||
// (do-xacts (xact entry)
|
|
||||||
// (when (xact-must-balance-p xact)
|
|
||||||
// (let ((amt (xact-amount* xact)))
|
|
||||||
// (if amt
|
|
||||||
// (setf balance (add balance (or (xact-cost xact) amt)))
|
|
||||||
// (if null-xact
|
|
||||||
// (error "Only one xact with null amount allowed ~
|
|
||||||
// per entry (beg ~S end ~S)"
|
|
||||||
// (item-position-begin-line (entry-position entry))
|
|
||||||
// (item-position-end-line (entry-position entry)))
|
|
||||||
// (setf null-xact xact))))))
|
|
||||||
//
|
|
||||||
|
|
||||||
foreach (xact_t * xact, xacts) {
|
foreach (xact_t * xact, xacts) {
|
||||||
if (xact->must_balance()) {
|
if (xact->must_balance()) {
|
||||||
amount_t& p(xact->cost ? *xact->cost : xact->amount);
|
amount_t& p(xact->cost ? *xact->cost : xact->amount);
|
||||||
|
|
@ -133,17 +120,6 @@ bool entry_base_t::finalize()
|
||||||
// If there is only one xact, balance against the default account if
|
// If there is only one xact, balance against the default account if
|
||||||
// one has been set.
|
// one has been set.
|
||||||
|
|
||||||
// (when (= 1 (length (entry-xacts entry)))
|
|
||||||
// (if-let ((default-account
|
|
||||||
// (journal-default-account (entry-journal entry))))
|
|
||||||
// (setf null-xact
|
|
||||||
// (make-xact :entry entry
|
|
||||||
// :status (xact-status
|
|
||||||
// (first (entry-xacts entry)))
|
|
||||||
// :account default-account
|
|
||||||
// :generatedp t))
|
|
||||||
// (add-xact entry null-xact)))
|
|
||||||
|
|
||||||
if (journal && journal->basket && xacts.size() == 1 && ! balance.is_null()) {
|
if (journal && journal->basket && xacts.size() == 1 && ! balance.is_null()) {
|
||||||
// jww (2008-07-24): Need to make the rest of the code aware of what to do
|
// jww (2008-07-24): Need to make the rest of the code aware of what to do
|
||||||
// when it sees a generated xact.
|
// when it sees a generated xact.
|
||||||
|
|
@ -157,24 +133,6 @@ bool entry_base_t::finalize()
|
||||||
// inverse of the rest. If multiple commodities are involved, multiple
|
// inverse of the rest. If multiple commodities are involved, multiple
|
||||||
// xacts are generated to balance them all.
|
// xacts are generated to balance them all.
|
||||||
|
|
||||||
// (progn
|
|
||||||
// (if (balance-p balance)
|
|
||||||
// (let ((first t))
|
|
||||||
// (dolist (amount (balance-amounts balance))
|
|
||||||
// (if first
|
|
||||||
// (setf (xact-amount* null-xact) (negate amount)
|
|
||||||
// first nil)
|
|
||||||
// (add-xact
|
|
||||||
// entry
|
|
||||||
// (make-xact :entry entry
|
|
||||||
// :account (xact-account null-xact)
|
|
||||||
// :amount (negate amount)
|
|
||||||
// :generatedp t)))))
|
|
||||||
// (setf (xact-amount* null-xact) (negate balance)
|
|
||||||
// (xact-calculatedp null-xact) t))
|
|
||||||
//
|
|
||||||
// (setf balance 0))
|
|
||||||
|
|
||||||
if (balance.is_balance()) {
|
if (balance.is_balance()) {
|
||||||
bool first = true;
|
bool first = true;
|
||||||
const balance_t& bal(balance.as_balance());
|
const balance_t& bal(balance.as_balance());
|
||||||
|
|
@ -206,21 +164,6 @@ bool entry_base_t::finalize()
|
||||||
// establishes the per-unit cost for this xact for both
|
// establishes the per-unit cost for this xact for both
|
||||||
// commodities.
|
// commodities.
|
||||||
|
|
||||||
// (when (and (balance-p balance)
|
|
||||||
// (= 2 (balance-commodity-count balance)))
|
|
||||||
// (destructuring-bind (x y) (balance-amounts balance)
|
|
||||||
// (let ((a-commodity (amount-commodity x))
|
|
||||||
// (per-unit-cost (value-abs (divide x y))))
|
|
||||||
// (do-xacts (xact entry)
|
|
||||||
// (let ((amount (xact-amount* xact)))
|
|
||||||
// (unless (or (xact-cost xact)
|
|
||||||
// (not (xact-must-balance-p xact))
|
|
||||||
// (commodity-equal (amount-commodity amount)
|
|
||||||
// a-commodity))
|
|
||||||
// (setf balance (subtract balance amount)
|
|
||||||
// (xact-cost xact) (multiply per-unit-cost amount)
|
|
||||||
// balance (add balance (xact-cost xact))))))))))
|
|
||||||
|
|
||||||
const balance_t& bal(balance.as_balance());
|
const balance_t& bal(balance.as_balance());
|
||||||
|
|
||||||
balance_t::amounts_map::const_iterator a = bal.amounts.begin();
|
balance_t::amounts_map::const_iterator a = bal.amounts.begin();
|
||||||
|
|
@ -234,22 +177,13 @@ bool entry_base_t::finalize()
|
||||||
commodity_t& comm(x.commodity());
|
commodity_t& comm(x.commodity());
|
||||||
|
|
||||||
foreach (xact_t * xact, xacts) {
|
foreach (xact_t * xact, xacts) {
|
||||||
const amount_t& x_amt(xact->amount);
|
const amount_t& amt(xact->amount);
|
||||||
|
|
||||||
if (! (xact->cost ||
|
|
||||||
! xact->must_balance() ||
|
|
||||||
x_amt.commodity() == comm)) {
|
|
||||||
DEBUG("entry.finalize", "before operation 1 = " << balance);
|
|
||||||
balance -= x_amt;
|
|
||||||
DEBUG("entry.finalize", "after operation 1 = " << balance);
|
|
||||||
DEBUG("entry.finalize", "x_amt = " << x_amt);
|
|
||||||
DEBUG("entry.finalize", "per_unit_cost = " << per_unit_cost);
|
|
||||||
|
|
||||||
xact->cost = per_unit_cost * x_amt;
|
|
||||||
DEBUG("entry.finalize", "*xact->cost = " << *xact->cost);
|
|
||||||
|
|
||||||
|
if (! (xact->cost || ! xact->must_balance() ||
|
||||||
|
amt.commodity() == comm)) {
|
||||||
|
balance -= amt;
|
||||||
|
xact->cost = per_unit_cost * amt;
|
||||||
balance += *xact->cost;
|
balance += *xact->cost;
|
||||||
DEBUG("entry.finalize", "after operation 2 = " << balance);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -262,58 +196,23 @@ bool entry_base_t::finalize()
|
||||||
// once more in terms of total cost, accounting for any possible gain/loss
|
// once more in terms of total cost, accounting for any possible gain/loss
|
||||||
// amounts.
|
// amounts.
|
||||||
|
|
||||||
// (do-xacts (xact entry)
|
|
||||||
// (when (xact-cost xact)
|
|
||||||
// (let ((amount (xact-amount* xact)))
|
|
||||||
// (assert (not (commodity-equal (amount-commodity amount)
|
|
||||||
// (amount-commodity (xact-cost xact)))))
|
|
||||||
// (multiple-value-bind (annotated-amount total-cost basis-cost)
|
|
||||||
// (exchange-commodity amount :total-cost (xact-cost xact)
|
|
||||||
// :moment (entry-date entry)
|
|
||||||
// :tag (entry-code entry))
|
|
||||||
// (if (annotated-commodity-p (amount-commodity amount))
|
|
||||||
// (if-let ((price (annotation-price
|
|
||||||
// (commodity-annotation
|
|
||||||
// (amount-commodity amount)))))
|
|
||||||
// (setf balance
|
|
||||||
// (add balance (subtract basis-cost total-cost))))
|
|
||||||
// (setf (xact-amount* xact) annotated-amount))))))
|
|
||||||
|
|
||||||
foreach (xact_t * xact, xacts) {
|
foreach (xact_t * xact, xacts) {
|
||||||
if (xact->cost) {
|
if (xact->cost) {
|
||||||
const amount_t& x_amt(xact->amount);
|
if (xact->amount.commodity() == xact->cost->commodity())
|
||||||
|
throw_(balance_error, "Transaction's cost must be of a different commodity");
|
||||||
|
|
||||||
assert(x_amt.commodity() != xact->cost->commodity());
|
commodity_t::cost_breakdown_t breakdown =
|
||||||
|
commodity_t::exchange(xact->amount, *xact->cost);
|
||||||
|
|
||||||
entry_t * entry = dynamic_cast<entry_t *>(this);
|
if (xact->amount.is_annotated())
|
||||||
|
add_or_set_value(balance, breakdown.basis_cost - breakdown.final_cost);
|
||||||
// jww (2008-07-24): Pass the entry's code here if we can, as the
|
else
|
||||||
// auto-tag
|
xact->amount = breakdown.amount;
|
||||||
amount_t final_cost;
|
|
||||||
amount_t basis_cost;
|
|
||||||
amount_t ann_amount =
|
|
||||||
commodity_t::exchange(x_amt, final_cost, basis_cost, xact->cost);
|
|
||||||
|
|
||||||
if (xact->amount.is_annotated()) {
|
|
||||||
if (ann_amount.annotation().price)
|
|
||||||
add_or_set_value(balance, basis_cost - final_cost);
|
|
||||||
} else {
|
|
||||||
xact->amount = ann_amount;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUG("entry.finalize", "final balance = " << balance);
|
DEBUG("entry.finalize", "final balance = " << balance);
|
||||||
|
|
||||||
// (if (value-zerop balance)
|
|
||||||
// (prog1
|
|
||||||
// entry
|
|
||||||
// (setf (entry-normalizedp entry) t))
|
|
||||||
// (error "Entry does not balance (beg ~S end ~S); remaining balance is:~%~A"
|
|
||||||
// (item-position-begin-line (entry-position entry))
|
|
||||||
// (item-position-end-line (entry-position entry))
|
|
||||||
// (format-value balance :width 20)))
|
|
||||||
|
|
||||||
if (! balance.is_null()) {
|
if (! balance.is_null()) {
|
||||||
balance.in_place_round();
|
balance.in_place_round();
|
||||||
if (! balance.is_zero()) {
|
if (! balance.is_zero()) {
|
||||||
|
|
|
||||||
|
|
@ -101,14 +101,6 @@ bool journal_t::add_entry(entry_t * entry)
|
||||||
|
|
||||||
entries.push_back(entry);
|
entries.push_back(entry);
|
||||||
|
|
||||||
foreach (const xact_t * xact, entry->xacts)
|
|
||||||
if (xact->cost) {
|
|
||||||
assert(xact->amount);
|
|
||||||
xact->amount.commodity().add_price(datetime_t(*entry->date(),
|
|
||||||
time_duration_t(0, 0, 0)),
|
|
||||||
*xact->cost / xact->amount.number());
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue