Cleaned up the way that commodity pricing is handled.

This commit is contained in:
John Wiegley 2008-09-20 13:48:36 -04:00
parent 0e95974360
commit 1bbb6933af
5 changed files with 64 additions and 153 deletions

View file

@ -241,6 +241,12 @@ public:
else
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);
#ifdef HAVE_GDTOA

View file

@ -43,8 +43,7 @@
namespace ledger {
void commodity_t::add_price(const datetime_t& date,
const amount_t& price)
void commodity_t::add_price(const datetime_t& date, const amount_t& price)
{
if (! base->history)
base->history = history_t();
@ -117,19 +116,26 @@ optional<amount_t> commodity_t::value(const optional<datetime_t>& moment)
return price;
}
amount_t commodity_t::exchange(const amount_t& amount,
amount_t& final_cost, // out
amount_t& basis_cost, // out
const optional<amount_t>& total_cost_,
const optional<amount_t>& per_unit_cost_,
const optional<datetime_t>& moment,
const optional<string>& tag)
void commodity_t::exchange(commodity_t& commodity,
const amount_t& per_unit_cost,
const datetime_t& moment)
{
// (assert (or (and total-cost (not per-unit-cost))
// (and per-unit-cost (not total-cost))))
if (! commodity.has_flags(COMMODITY_STYLE_NOMARKET)) {
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))
// (current-annotation
// (and (annotated-commodity-p commodity)
@ -152,9 +158,10 @@ amount_t commodity_t::exchange(const amount_t& amount,
(current_annotation ?
as_annotated_commodity(commodity).referent() : commodity);
amount_t per_unit_cost(per_unit_cost_ ?
*per_unit_cost_ : *total_cost_ / amount);
final_cost = total_cost_ ? *total_cost_ : *per_unit_cost_ * amount;
amount_t per_unit_cost(is_per_unit ? cost : 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
// place
@ -177,16 +184,15 @@ amount_t commodity_t::exchange(const amount_t& amount,
// total-cost))))
if (current_annotation && current_annotation->price)
basis_cost = *current_annotation->price * amount;
breakdown.basis_cost = *current_annotation->price * amount;
else
basis_cost = final_cost;
breakdown.basis_cost = breakdown.final_cost;
amount_t ann_amount(amount);
ann_amount.annotate
(annotation_t(per_unit_cost,
moment ? moment->date() : optional<date_t>(), tag));
breakdown.amount =
amount_t(amount, annotation_t (per_unit_cost, moment ?
moment->date() : optional<date_t>(), tag));
return ann_amount;
return breakdown;
}
commodity_t::operator bool() const

View file

@ -192,18 +192,26 @@ public:
return base->history;
}
void add_price(const datetime_t& date, const amount_t& price);
bool remove_price(const datetime_t& date);
void add_price(const datetime_t& date, const amount_t& price);
bool remove_price(const datetime_t& date);
optional<amount_t> value(const optional<datetime_t>& moment = none);
static amount_t exchange(const amount_t& amount,
amount_t& final_cost, // out
amount_t& basis_cost, // out
const optional<amount_t>& total_cost,
const optional<amount_t>& per_unit_cost = none,
const optional<datetime_t>& moment = none,
const optional<string>& tag = none);
struct cost_breakdown_t {
amount_t amount;
amount_t final_cost;
amount_t basis_cost;
};
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(char *& p, string& symbol);

View file

@ -98,19 +98,6 @@ bool entry_base_t::finalize()
value_t balance;
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) {
if (xact->must_balance()) {
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
// 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()) {
// jww (2008-07-24): Need to make the rest of the code aware of what to do
// 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
// 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()) {
bool first = true;
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
// 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());
balance_t::amounts_map::const_iterator a = bal.amounts.begin();
@ -234,22 +177,13 @@ bool entry_base_t::finalize()
commodity_t& comm(x.commodity());
foreach (xact_t * xact, xacts) {
const amount_t& x_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);
const amount_t& amt(xact->amount);
if (! (xact->cost || ! xact->must_balance() ||
amt.commodity() == comm)) {
balance -= amt;
xact->cost = per_unit_cost * amt;
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
// 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) {
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);
// jww (2008-07-24): Pass the entry's code here if we can, as the
// auto-tag
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;
}
if (xact->amount.is_annotated())
add_or_set_value(balance, breakdown.basis_cost - breakdown.final_cost);
else
xact->amount = breakdown.amount;
}
}
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()) {
balance.in_place_round();
if (! balance.is_zero()) {

View file

@ -101,14 +101,6 @@ bool journal_t::add_entry(entry_t * 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;
}