Parsing now works again. And there was much rejoicing.
This commit is contained in:
parent
171f79dda2
commit
ee39695722
11 changed files with 365 additions and 167 deletions
19
amount.cc
19
amount.cc
|
|
@ -855,19 +855,18 @@ bool amount_t::commodity_annotated() const
|
||||||
return commodity().annotated;
|
return commodity().annotated;
|
||||||
}
|
}
|
||||||
|
|
||||||
annotation_t amount_t::annotation_details() const
|
annotation_t& amount_t::annotation_details()
|
||||||
{
|
{
|
||||||
if (! quantity)
|
if (! quantity)
|
||||||
throw_(amount_error,
|
throw_(amount_error,
|
||||||
"Cannot return commodity annotation details of an uninitialized amount");
|
"Cannot return commodity annotation details of an uninitialized amount");
|
||||||
|
|
||||||
assert(! commodity().annotated || as_annotated_commodity(commodity()).details);
|
if (! commodity().is_annotated())
|
||||||
|
throw_(amount_error,
|
||||||
|
"Request for annotation details from an unannotated amount");
|
||||||
|
|
||||||
if (commodity().annotated) {
|
annotated_commodity_t& ann_comm(as_annotated_commodity(commodity()));
|
||||||
annotated_commodity_t& ann_comm(as_annotated_commodity(commodity()));
|
return ann_comm.details;
|
||||||
return ann_comm.details;
|
|
||||||
}
|
|
||||||
return annotation_t();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
amount_t amount_t::strip_annotations(const bool _keep_price,
|
amount_t amount_t::strip_annotations(const bool _keep_price,
|
||||||
|
|
@ -1097,8 +1096,10 @@ void amount_t::print(std::ostream& _out, bool omit_commodity,
|
||||||
{
|
{
|
||||||
assert(valid());
|
assert(valid());
|
||||||
|
|
||||||
if (! quantity)
|
if (! quantity) {
|
||||||
throw_(amount_error, "Cannot write out an uninitialized amount");
|
_out << "<null>";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
amount_t base(*this);
|
amount_t base(*this);
|
||||||
if (! amount_t::keep_base)
|
if (! amount_t::keep_base)
|
||||||
|
|
|
||||||
17
amount.h
17
amount.h
|
|
@ -538,12 +538,17 @@ public:
|
||||||
* amount_t::keep_price, amount_t::keep_date and amount_t::keep_tag
|
* amount_t::keep_price, amount_t::keep_date and amount_t::keep_tag
|
||||||
* have been set to (which all default to false).
|
* have been set to (which all default to false).
|
||||||
*/
|
*/
|
||||||
void annotate_commodity(const annotation_t& details);
|
void annotate_commodity(const annotation_t& details);
|
||||||
bool commodity_annotated() const;
|
bool commodity_annotated() const;
|
||||||
annotation_t annotation_details() const;
|
|
||||||
amount_t strip_annotations(const bool _keep_price = keep_price,
|
annotation_t& annotation_details();
|
||||||
const bool _keep_date = keep_date,
|
const annotation_t& annotation_details() const {
|
||||||
const bool _keep_tag = keep_tag) const;
|
return const_cast<amount_t&>(*this).annotation_details();
|
||||||
|
}
|
||||||
|
|
||||||
|
amount_t strip_annotations(const bool _keep_price = keep_price,
|
||||||
|
const bool _keep_date = keep_date,
|
||||||
|
const bool _keep_tag = keep_tag) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parsing methods. The method `parse' is used to parse an amount
|
* Parsing methods. The method `parse' is used to parse an amount
|
||||||
|
|
|
||||||
69
commodity.cc
69
commodity.cc
|
|
@ -118,6 +118,75 @@ optional<amount_t> commodity_t::value(const optional<datetime_t>& moment)
|
||||||
return price;
|
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)
|
||||||
|
{
|
||||||
|
// (assert (or (and total-cost (not per-unit-cost))
|
||||||
|
// (and per-unit-cost (not total-cost))))
|
||||||
|
|
||||||
|
assert((total_cost_ && ! per_unit_cost_) || (per_unit_cost_ && ! total_cost_));
|
||||||
|
|
||||||
|
// (let* ((commodity (amount-commodity amount))
|
||||||
|
// (current-annotation
|
||||||
|
// (and (annotated-commodity-p commodity)
|
||||||
|
// (commodity-annotation commodity)))
|
||||||
|
// (base-commodity (if (annotated-commodity-p commodity)
|
||||||
|
// (get-referent commodity)
|
||||||
|
// commodity))
|
||||||
|
// (per-unit-cost (or per-unit-cost
|
||||||
|
// (divide total-cost amount)))
|
||||||
|
// (total-cost (or total-cost
|
||||||
|
// (multiply per-unit-cost amount))))
|
||||||
|
|
||||||
|
commodity_t& commodity(amount.commodity());
|
||||||
|
|
||||||
|
annotation_t * current_annotation = NULL;
|
||||||
|
if (commodity.annotated)
|
||||||
|
current_annotation = &as_annotated_commodity(commodity).details;
|
||||||
|
|
||||||
|
commodity_t& base_commodity
|
||||||
|
(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;
|
||||||
|
|
||||||
|
// Add a price history entry for this conversion if we know when it took
|
||||||
|
// place
|
||||||
|
|
||||||
|
// (if (and moment (not (commodity-no-market-price-p base-commodity)))
|
||||||
|
// (add-price base-commodity per-unit-cost moment))
|
||||||
|
|
||||||
|
if (moment && ! commodity.has_flags(COMMODITY_STYLE_NOMARKET))
|
||||||
|
base_commodity.add_price(*moment, per_unit_cost);
|
||||||
|
|
||||||
|
// ;; returns: ANNOTATED-AMOUNT TOTAL-COST BASIS-COST
|
||||||
|
// (values (annotate-commodity
|
||||||
|
// amount
|
||||||
|
// (make-commodity-annotation :price per-unit-cost
|
||||||
|
// :date moment
|
||||||
|
// :tag tag))
|
||||||
|
// total-cost
|
||||||
|
// (if current-annotation
|
||||||
|
// (multiply (annotation-price current-annotation) amount)
|
||||||
|
// total-cost))))
|
||||||
|
|
||||||
|
if (current_annotation && current_annotation->price)
|
||||||
|
basis_cost = *current_annotation->price * amount;
|
||||||
|
else
|
||||||
|
basis_cost = final_cost;
|
||||||
|
|
||||||
|
amount_t ann_amount(amount);
|
||||||
|
ann_amount.annotate_commodity(annotation_t(per_unit_cost, moment, tag));
|
||||||
|
return ann_amount;
|
||||||
|
}
|
||||||
|
|
||||||
commodity_t::operator bool() const
|
commodity_t::operator bool() const
|
||||||
{
|
{
|
||||||
return this != parent().null_commodity;
|
return this != parent().null_commodity;
|
||||||
|
|
|
||||||
12
commodity.h
12
commodity.h
|
|
@ -117,6 +117,10 @@ public:
|
||||||
|
|
||||||
operator bool() const;
|
operator bool() const;
|
||||||
|
|
||||||
|
bool is_annotated() const {
|
||||||
|
return annotated;
|
||||||
|
}
|
||||||
|
|
||||||
virtual bool operator==(const commodity_t& comm) const {
|
virtual bool operator==(const commodity_t& comm) const {
|
||||||
if (comm.annotated)
|
if (comm.annotated)
|
||||||
return comm == *this;
|
return comm == *this;
|
||||||
|
|
@ -185,6 +189,14 @@ public:
|
||||||
|
|
||||||
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,
|
||||||
|
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);
|
||||||
|
|
||||||
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);
|
||||||
static string parse_symbol(std::istream& in) {
|
static string parse_symbol(std::istream& in) {
|
||||||
|
|
|
||||||
320
journal.cc
320
journal.cc
|
|
@ -118,185 +118,249 @@ bool entry_base_t::remove_transaction(transaction_t * xact)
|
||||||
|
|
||||||
bool entry_base_t::finalize()
|
bool entry_base_t::finalize()
|
||||||
{
|
{
|
||||||
// Scan through and compute the total balance for the entry. This
|
// Scan through and compute the total balance for the entry. This is used
|
||||||
// is used for auto-calculating the value of entries with no cost,
|
// for auto-calculating the value of entries with no cost, and the per-unit
|
||||||
// and the per-unit price of unpriced commodities.
|
// price of unpriced commodities.
|
||||||
|
|
||||||
value_t balance;
|
// (let ((balance 0)
|
||||||
bool no_amounts = true;
|
// null-xact)
|
||||||
bool saw_null = false;
|
|
||||||
|
value_t balance;
|
||||||
|
transaction_t * null_xact = NULL;
|
||||||
|
|
||||||
|
// (do-transactions (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 transaction 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))))))
|
||||||
|
//
|
||||||
|
|
||||||
for (transactions_list::const_iterator x = transactions.begin();
|
for (transactions_list::const_iterator x = transactions.begin();
|
||||||
x != transactions.end();
|
x != transactions.end();
|
||||||
x++) {
|
x++) {
|
||||||
if (! (*x)->has_flags(TRANSACTION_VIRTUAL) ||
|
if ((*x)->must_balance()) {
|
||||||
(*x)->has_flags(TRANSACTION_BALANCE)) {
|
|
||||||
amount_t& p((*x)->cost ? *(*x)->cost : (*x)->amount);
|
amount_t& p((*x)->cost ? *(*x)->cost : (*x)->amount);
|
||||||
if (! p.is_null()) {
|
if (! p.is_null()) {
|
||||||
if (no_amounts) {
|
if (balance.is_null())
|
||||||
balance = p;
|
balance = p;
|
||||||
no_amounts = false;
|
else
|
||||||
} else {
|
|
||||||
balance += p;
|
balance += p;
|
||||||
}
|
|
||||||
|
|
||||||
assert(! (*x)->amount.is_null());
|
|
||||||
|
|
||||||
if ((*x)->cost && (*x)->amount.commodity().annotated) {
|
|
||||||
annotated_commodity_t&
|
|
||||||
ann_comm(static_cast<annotated_commodity_t&>
|
|
||||||
((*x)->amount.commodity()));
|
|
||||||
if (ann_comm.details.price)
|
|
||||||
balance += (*ann_comm.details.price * (*x)->amount.number() -
|
|
||||||
*((*x)->cost));
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
saw_null = true;
|
if (null_xact)
|
||||||
|
throw_(std::logic_error,
|
||||||
|
"Only one transaction with null amount allowed per entry");
|
||||||
|
else
|
||||||
|
null_xact = *x;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(balance.valid());
|
assert(balance.valid());
|
||||||
|
|
||||||
// If it's a null entry, then let the user have their fun
|
DEBUG("ledger.journal.finalize", "initial balance = " << balance);
|
||||||
if (no_amounts)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// If there is only one transaction, balance against the basket
|
// If there is only one transaction, balance against the default account if
|
||||||
// account if one has been set.
|
// one has been set.
|
||||||
|
|
||||||
|
// (when (= 1 (length (entry-transactions entry)))
|
||||||
|
// (if-let ((default-account
|
||||||
|
// (journal-default-account (entry-journal entry))))
|
||||||
|
// (setf null-xact
|
||||||
|
// (make-transaction :entry entry
|
||||||
|
// :status (xact-status
|
||||||
|
// (first (entry-transactions entry)))
|
||||||
|
// :account default-account
|
||||||
|
// :generatedp t))
|
||||||
|
// (add-transaction entry null-xact)))
|
||||||
|
|
||||||
if (journal && journal->basket && transactions.size() == 1) {
|
if (journal && journal->basket && transactions.size() == 1) {
|
||||||
assert(balance.is_amount());
|
// jww (2008-07-24): Need to make the rest of the code aware of what to do
|
||||||
transaction_t * nxact = new transaction_t(journal->basket);
|
// when it sees a generated transaction.
|
||||||
// The amount doesn't need to be set because the code below will
|
null_xact = new transaction_t(journal->basket, TRANSACTION_GENERATED);
|
||||||
// balance this transaction against the other.
|
null_xact->state = (*transactions.begin())->state;
|
||||||
add_transaction(nxact);
|
add_transaction(null_xact);
|
||||||
nxact->add_flags(TRANSACTION_CALCULATED);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the first transaction of a two-transaction entry is of a
|
if (null_xact != NULL) {
|
||||||
// different commodity than the other, and it has no per-unit price,
|
// If one transaction has no value at all, its value will become the
|
||||||
// determine its price by dividing the unit count into the value of
|
// inverse of the rest. If multiple commodities are involved, multiple
|
||||||
// the balance. This is done for the last eligible commodity.
|
// transactions 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-transaction
|
||||||
|
// entry
|
||||||
|
// (make-transaction :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());
|
||||||
|
for (balance_t::amounts_map::const_iterator i = bal.amounts.begin();
|
||||||
|
i != bal.amounts.end();
|
||||||
|
i++) {
|
||||||
|
if (first) {
|
||||||
|
null_xact->amount = (*i).second.negate();
|
||||||
|
first = false;
|
||||||
|
} else {
|
||||||
|
add_transaction(new transaction_t(null_xact->account,
|
||||||
|
(*i).second.negate(),
|
||||||
|
TRANSACTION_GENERATED));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
null_xact->amount = balance.as_amount().negate();
|
||||||
|
null_xact->add_flags(TRANSACTION_CALCULATED);
|
||||||
|
}
|
||||||
|
balance = NULL_VALUE;
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (balance.is_balance() &&
|
||||||
|
balance.as_balance().amounts.size() == 2) {
|
||||||
|
// When an entry involves two different commodities (regardless of how
|
||||||
|
// many transactions there are) determine the conversion ratio by dividing
|
||||||
|
// the total value of one commodity by the total value of the other. This
|
||||||
|
// establishes the per-unit cost for this transaction 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-transactions (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))))))))))
|
||||||
|
|
||||||
if (! saw_null && balance && balance.is_balance()) {
|
|
||||||
const balance_t& bal(balance.as_balance());
|
const balance_t& bal(balance.as_balance());
|
||||||
if (bal.amounts.size() == 2) {
|
|
||||||
transactions_list::const_iterator x = transactions.begin();
|
|
||||||
assert(! (*x)->amount.is_null());
|
|
||||||
commodity_t& this_comm = (*x)->amount.commodity();
|
|
||||||
|
|
||||||
balance_t::amounts_map::const_iterator this_bal =
|
balance_t::amounts_map::const_iterator a = bal.amounts.begin();
|
||||||
bal.amounts.find(&this_comm);
|
|
||||||
assert(this_bal != bal.amounts.end());
|
const amount_t& x((*a++).second);
|
||||||
|
const amount_t& y((*a++).second);
|
||||||
|
|
||||||
balance_t::amounts_map::const_iterator other_bal =
|
if (! y.is_realzero()) {
|
||||||
bal.amounts.begin();
|
amount_t per_unit_cost = (x / y).abs();
|
||||||
|
|
||||||
if (this_bal == other_bal)
|
|
||||||
other_bal++;
|
|
||||||
|
|
||||||
amount_t per_unit_cost =
|
commodity_t& comm(x.commodity());
|
||||||
((*other_bal).second / (*this_bal).second.number()).unround();
|
|
||||||
|
|
||||||
for (; x != transactions.end(); x++) {
|
for (transactions_list::const_iterator x = transactions.begin();
|
||||||
if ((*x)->cost || (*x)->has_flags(TRANSACTION_VIRTUAL) ||
|
x != transactions.end();
|
||||||
(*x)->amount.commodity() != this_comm)
|
x++) {
|
||||||
continue;
|
const amount_t& x_amt((*x)->amount);
|
||||||
|
|
||||||
balance -= (*x)->amount;
|
if (! ((*x)->cost ||
|
||||||
|
! (*x)->must_balance() ||
|
||||||
|
x_amt.commodity() == comm)) {
|
||||||
|
DEBUG("ledger.journal.finalize", "before operation 1 = " << balance);
|
||||||
|
balance -= x_amt;
|
||||||
|
DEBUG("ledger.journal.finalize", "after operation 1 = " << balance);
|
||||||
|
DEBUG("ledger.journal.finalize", "x_amt = " << x_amt);
|
||||||
|
DEBUG("ledger.journal.finalize", "per_unit_cost = " << per_unit_cost);
|
||||||
|
|
||||||
entry_t * entry = dynamic_cast<entry_t *>(this);
|
(*x)->cost = per_unit_cost * x_amt;
|
||||||
|
DEBUG("ledger.journal.finalize", "*(*x)->cost = " << *(*x)->cost);
|
||||||
|
|
||||||
if ((*x)->amount.commodity() &&
|
balance += *(*x)->cost;
|
||||||
! (*x)->amount.commodity().annotated)
|
DEBUG("ledger.journal.finalize", "after operation 2 = " << balance);
|
||||||
(*x)->amount.annotate_commodity
|
}
|
||||||
(annotation_t(per_unit_cost.abs(),
|
|
||||||
entry ? entry->actual_date() : optional<datetime_t>(),
|
|
||||||
entry ? entry->code : optional<string>()));
|
|
||||||
|
|
||||||
(*x)->cost = - (per_unit_cost * (*x)->amount.number());
|
|
||||||
balance += *(*x)->cost;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEBUG("ledger.journal.finalize", "resolved balance = " << balance);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Walk through each of the transactions, fixing up any that we
|
// Now that the transaction list has its final form, calculate the balance
|
||||||
// can, and performing any on-the-fly calculations.
|
// once more in terms of total cost, accounting for any possible gain/loss
|
||||||
|
// amounts.
|
||||||
|
|
||||||
bool empty_allowed = true;
|
// (do-transactions (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))))))
|
||||||
|
|
||||||
for (transactions_list::const_iterator x = transactions.begin();
|
for (transactions_list::const_iterator x = transactions.begin();
|
||||||
x != transactions.end();
|
x != transactions.end();
|
||||||
x++) {
|
x++) {
|
||||||
if (! (*x)->amount.is_null() ||
|
if ((*x)->cost) {
|
||||||
((*x)->has_flags(TRANSACTION_VIRTUAL) &&
|
const amount_t& x_amt((*x)->amount);
|
||||||
! (*x)->has_flags(TRANSACTION_BALANCE)))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (! empty_allowed)
|
assert(x_amt.commodity() != (*x)->cost->commodity());
|
||||||
throw_(std::logic_error,
|
|
||||||
"Only one transaction with null amount allowed per entry");
|
|
||||||
empty_allowed = false;
|
|
||||||
|
|
||||||
// If one transaction gives no value at all, its value will become
|
entry_t * entry = dynamic_cast<entry_t *>(this);
|
||||||
// the inverse of the value of the others. If multiple
|
|
||||||
// commodities are involved, multiple transactions will be
|
|
||||||
// generated to balance them all.
|
|
||||||
|
|
||||||
const balance_t * bal = NULL;
|
// jww (2008-07-24): Pass the entry's code here if we can, as the
|
||||||
switch (balance.type()) {
|
// auto-tag
|
||||||
case value_t::BALANCE_PAIR:
|
amount_t final_cost;
|
||||||
bal = &balance.as_balance_pair().quantity();
|
amount_t basis_cost;
|
||||||
// fall through...
|
amount_t ann_amount =
|
||||||
|
commodity_t::exchange(x_amt, final_cost, basis_cost,
|
||||||
|
(*x)->cost, none, (*x)->actual_date(),
|
||||||
|
entry ? entry->code : optional<string>());
|
||||||
|
|
||||||
case value_t::BALANCE:
|
if ((*x)->amount.commodity_annotated()) {
|
||||||
if (! bal)
|
if (ann_amount.annotation_details().price) {
|
||||||
bal = &balance.as_balance();
|
if (balance.is_null())
|
||||||
|
balance = basis_cost - final_cost;
|
||||||
if (bal->amounts.size() < 2) {
|
else
|
||||||
balance.cast(value_t::AMOUNT);
|
balance += basis_cost - final_cost;
|
||||||
} else {
|
|
||||||
bool first = true;
|
|
||||||
for (balance_t::amounts_map::const_iterator
|
|
||||||
i = bal->amounts.begin();
|
|
||||||
i != bal->amounts.end();
|
|
||||||
i++) {
|
|
||||||
amount_t amt = (*i).second.negate();
|
|
||||||
|
|
||||||
if (first) {
|
|
||||||
(*x)->amount = amt;
|
|
||||||
first = false;
|
|
||||||
} else {
|
|
||||||
transaction_t * nxact = new transaction_t((*x)->account);
|
|
||||||
add_transaction(nxact);
|
|
||||||
nxact->add_flags(TRANSACTION_CALCULATED);
|
|
||||||
nxact->amount = amt;
|
|
||||||
}
|
|
||||||
|
|
||||||
balance += amt;
|
|
||||||
}
|
}
|
||||||
break;
|
} else {
|
||||||
|
(*x)->amount = ann_amount;
|
||||||
}
|
}
|
||||||
// fall through...
|
|
||||||
|
|
||||||
case value_t::AMOUNT:
|
|
||||||
(*x)->amount = balance.as_amount().negate();
|
|
||||||
(*x)->add_flags(TRANSACTION_CALCULATED);
|
|
||||||
|
|
||||||
balance += (*x)->amount;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (balance) {
|
DEBUG("ledger.journal.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.is_zero()) {
|
||||||
error * err =
|
error * err =
|
||||||
new balance_error("Entry does not balance",
|
new balance_error("Entry does not balance",
|
||||||
new entry_context(*this, "While balancing entry:"));
|
new entry_context(*this, "While balancing entry:"));
|
||||||
DEBUG("ledger.journal.unbalanced_remainder", "balance = " << balance);
|
|
||||||
balance.round();
|
balance.round();
|
||||||
err->context.push_front
|
err->context.push_front
|
||||||
(new value_context(balance, "Unbalanced remainder is:"));
|
(new value_context(balance, "Unbalanced remainder is:"));
|
||||||
|
|
|
||||||
10
journal.h
10
journal.h
|
|
@ -46,6 +46,7 @@ namespace ledger {
|
||||||
#define TRANSACTION_AUTO 0x0004
|
#define TRANSACTION_AUTO 0x0004
|
||||||
#define TRANSACTION_BULK_ALLOC 0x0008
|
#define TRANSACTION_BULK_ALLOC 0x0008
|
||||||
#define TRANSACTION_CALCULATED 0x0010
|
#define TRANSACTION_CALCULATED 0x0010
|
||||||
|
#define TRANSACTION_GENERATED 0x0020
|
||||||
|
|
||||||
class entry_t;
|
class entry_t;
|
||||||
class account_t;
|
class account_t;
|
||||||
|
|
@ -73,8 +74,9 @@ class transaction_t : public supports_flags<>
|
||||||
mutable void * data;
|
mutable void * data;
|
||||||
static bool use_effective_date;
|
static bool use_effective_date;
|
||||||
|
|
||||||
transaction_t(account_t * _account = NULL)
|
transaction_t(account_t * _account = NULL,
|
||||||
: supports_flags<>(TRANSACTION_NORMAL), entry(NULL),
|
unsigned int _flags = TRANSACTION_NORMAL)
|
||||||
|
: supports_flags<>(_flags), entry(NULL),
|
||||||
state(UNCLEARED), account(_account),
|
state(UNCLEARED), account(_account),
|
||||||
beg_pos(0), beg_line(0), end_pos(0), end_line(0), data(NULL)
|
beg_pos(0), beg_line(0), end_pos(0), end_line(0), data(NULL)
|
||||||
{
|
{
|
||||||
|
|
@ -120,6 +122,10 @@ class transaction_t : public supports_flags<>
|
||||||
return actual_date();
|
return actual_date();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool must_balance() const {
|
||||||
|
return ! has_flags(TRANSACTION_VIRTUAL) || has_flags(TRANSACTION_BALANCE);
|
||||||
|
}
|
||||||
|
|
||||||
bool valid() const;
|
bool valid() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
5
main.cc
5
main.cc
|
|
@ -205,12 +205,15 @@ static int read_and_report(ledger::report_t& report, int argc, char * argv[],
|
||||||
|
|
||||||
journal_t& journal(*session.create_journal());
|
journal_t& journal(*session.create_journal());
|
||||||
|
|
||||||
if (! session.read_data(journal, report.account))
|
std::size_t count = session.read_data(journal, report.account);
|
||||||
|
if (count == 0)
|
||||||
throw_(parse_error, "Failed to locate any journal entries; "
|
throw_(parse_error, "Failed to locate any journal entries; "
|
||||||
"did you specify a valid file with -f?");
|
"did you specify a valid file with -f?");
|
||||||
|
|
||||||
INFO_FINISH(journal);
|
INFO_FINISH(journal);
|
||||||
|
|
||||||
|
INFO("Found " << count << " entries");
|
||||||
|
|
||||||
TRACE_FINISH(entry_text, 1);
|
TRACE_FINISH(entry_text, 1);
|
||||||
TRACE_FINISH(entry_date, 1);
|
TRACE_FINISH(entry_date, 1);
|
||||||
TRACE_FINISH(entry_details, 1);
|
TRACE_FINISH(entry_details, 1);
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ value_t report_t::abbrev(expr::call_scope_t& args)
|
||||||
return value_t(abbreviate(str, wid, style, true,
|
return value_t(abbreviate(str, wid, style, true,
|
||||||
static_cast<int>(abbrev_len)), true);
|
static_cast<int>(abbrev_len)), true);
|
||||||
#else
|
#else
|
||||||
return value_t();
|
return NULL_VALUE;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ value_t scope_t::resolve(const string& name) {
|
||||||
if (definition)
|
if (definition)
|
||||||
return definition->calc(*this);
|
return definition->calc(*this);
|
||||||
else
|
else
|
||||||
return value_t();
|
return NULL_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void symbol_scope_t::define(const string& name, ptr_op_t def)
|
void symbol_scope_t::define(const string& name, ptr_op_t def)
|
||||||
|
|
@ -135,7 +135,7 @@ namespace {
|
||||||
temp.reset(expr);
|
temp.reset(expr);
|
||||||
} else {
|
} else {
|
||||||
temp.reset(new op_t(op_t::VALUE));
|
temp.reset(new op_t(op_t::VALUE));
|
||||||
temp->set_value(value_t());
|
temp->set_value(NULL_VALUE);
|
||||||
expr->compute(temp->as_value_lval(), details, context);
|
expr->compute(temp->as_value_lval(), details, context);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
73
value.cc
73
value.cc
|
|
@ -298,7 +298,9 @@ void value_t::in_place_simplify()
|
||||||
|
|
||||||
if (is_balance() && as_balance().amounts.size() == 1) {
|
if (is_balance() && as_balance().amounts.size() == 1) {
|
||||||
DEBUG_("Reducing balance to amount");
|
DEBUG_("Reducing balance to amount");
|
||||||
|
DEBUG("ledger.value.reduce", "as a balance it looks like: " << *this);
|
||||||
in_place_cast(AMOUNT);
|
in_place_cast(AMOUNT);
|
||||||
|
DEBUG("ledger.value.reduce", "as an amount it looks like: " << *this);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
|
|
@ -445,7 +447,7 @@ value_t& value_t::operator+=(const value_t& val)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw_(value_error, "Cannot add " << label() << " to " << val.label());
|
throw_(value_error, "Cannot add " << val.label() << " to " << label());
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
@ -605,7 +607,7 @@ value_t& value_t::operator-=(const value_t& val)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw_(value_error, "Cannot subtract " << label() << " from " << val.label());
|
throw_(value_error, "Cannot subtract " << val.label() << " from " << label());
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
@ -952,8 +954,7 @@ bool value_t::operator>(const value_t& val) const
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw_(value_error,
|
throw_(value_error, "Cannot compare " << label() << " to " << val.label());
|
||||||
"Cannot compare " << label() << " to " << val.label());
|
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
@ -1019,13 +1020,13 @@ void value_t::in_place_cast(type_t cast_type)
|
||||||
if (amt.is_null())
|
if (amt.is_null())
|
||||||
set_balance(balance_t());
|
set_balance(balance_t());
|
||||||
else
|
else
|
||||||
set_balance(as_amount());
|
set_balance(as_amount()); // creates temporary
|
||||||
return;
|
return;
|
||||||
case BALANCE_PAIR:
|
case BALANCE_PAIR:
|
||||||
if (amt.is_null())
|
if (amt.is_null())
|
||||||
set_balance_pair(balance_pair_t());
|
set_balance_pair(balance_pair_t());
|
||||||
else
|
else
|
||||||
set_balance_pair(as_amount());
|
set_balance_pair(as_amount()); // creates temporary
|
||||||
return;
|
return;
|
||||||
case STRING:
|
case STRING:
|
||||||
if (amt.is_null())
|
if (amt.is_null())
|
||||||
|
|
@ -1044,7 +1045,10 @@ void value_t::in_place_cast(type_t cast_type)
|
||||||
case AMOUNT: {
|
case AMOUNT: {
|
||||||
const balance_t& temp(as_balance());
|
const balance_t& temp(as_balance());
|
||||||
if (temp.amounts.size() == 1) {
|
if (temp.amounts.size() == 1) {
|
||||||
set_amount((*temp.amounts.begin()).second);
|
// Because we are changing the current balance value to an amount
|
||||||
|
// value, and because set_amount takes a reference (and that memory is
|
||||||
|
// about to be repurposed), we must pass in a copy.
|
||||||
|
set_amount(amount_t((*temp.amounts.begin()).second));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (temp.amounts.size() == 0) {
|
else if (temp.amounts.size() == 0) {
|
||||||
|
|
@ -1070,7 +1074,7 @@ void value_t::in_place_cast(type_t cast_type)
|
||||||
case AMOUNT: {
|
case AMOUNT: {
|
||||||
const balance_t& temp(as_balance_pair().quantity());
|
const balance_t& temp(as_balance_pair().quantity());
|
||||||
if (temp.amounts.size() == 1) {
|
if (temp.amounts.size() == 1) {
|
||||||
set_amount((*temp.amounts.begin()).second);
|
set_amount(amount_t((*temp.amounts.begin()).second));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (temp.amounts.size() == 0) {
|
else if (temp.amounts.size() == 0) {
|
||||||
|
|
@ -1084,7 +1088,9 @@ void value_t::in_place_cast(type_t cast_type)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case BALANCE:
|
case BALANCE:
|
||||||
set_balance(as_balance_pair().quantity());
|
// A temporary is required, becaues set_balance is going to wipe us out
|
||||||
|
// before assigned the value passed in.
|
||||||
|
set_balance(balance_t(as_balance_pair().quantity()));
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
@ -1176,6 +1182,37 @@ bool value_t::is_realzero() const
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool value_t::is_zero() const
|
||||||
|
{
|
||||||
|
switch (type()) {
|
||||||
|
case BOOLEAN:
|
||||||
|
return ! as_boolean();
|
||||||
|
case INTEGER:
|
||||||
|
return as_long() == 0;
|
||||||
|
case DATETIME:
|
||||||
|
return ! is_valid(as_datetime());
|
||||||
|
case AMOUNT:
|
||||||
|
return as_amount().is_zero();
|
||||||
|
case BALANCE:
|
||||||
|
return as_balance().is_zero();
|
||||||
|
case BALANCE_PAIR:
|
||||||
|
return as_balance_pair().is_zero();
|
||||||
|
case STRING:
|
||||||
|
return as_string().empty();
|
||||||
|
case SEQUENCE:
|
||||||
|
return as_sequence().empty();
|
||||||
|
|
||||||
|
case POINTER:
|
||||||
|
return as_any_pointer().empty();
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
assert(false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
value_t value_t::value(const optional<datetime_t>& moment) const
|
value_t value_t::value(const optional<datetime_t>& moment) const
|
||||||
{
|
{
|
||||||
switch (type()) {
|
switch (type()) {
|
||||||
|
|
@ -1204,7 +1241,7 @@ value_t value_t::value(const optional<datetime_t>& moment) const
|
||||||
}
|
}
|
||||||
|
|
||||||
throw_(value_error, "Cannot find the value of " << label());
|
throw_(value_error, "Cannot find the value of " << label());
|
||||||
return value_t();
|
return NULL_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void value_t::in_place_reduce()
|
void value_t::in_place_reduce()
|
||||||
|
|
@ -1248,7 +1285,7 @@ value_t value_t::abs() const
|
||||||
}
|
}
|
||||||
|
|
||||||
throw_(value_error, "Cannot abs " << label());
|
throw_(value_error, "Cannot abs " << label());
|
||||||
return value_t();
|
return NULL_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
value_t value_t::round() const
|
value_t value_t::round() const
|
||||||
|
|
@ -1267,7 +1304,7 @@ value_t value_t::round() const
|
||||||
}
|
}
|
||||||
|
|
||||||
throw_(value_error, "Cannot round " << label());
|
throw_(value_error, "Cannot round " << label());
|
||||||
return value_t();
|
return NULL_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
value_t value_t::unround() const
|
value_t value_t::unround() const
|
||||||
|
|
@ -1282,7 +1319,7 @@ value_t value_t::unround() const
|
||||||
}
|
}
|
||||||
|
|
||||||
throw_(value_error, "Cannot unround " << label());
|
throw_(value_error, "Cannot unround " << label());
|
||||||
return value_t();
|
return NULL_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
value_t value_t::annotated_price() const
|
value_t value_t::annotated_price() const
|
||||||
|
|
@ -1300,7 +1337,7 @@ value_t value_t::annotated_price() const
|
||||||
}
|
}
|
||||||
|
|
||||||
throw_(value_error, "Cannot find the annotated price of " << label());
|
throw_(value_error, "Cannot find the annotated price of " << label());
|
||||||
return value_t();
|
return NULL_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
value_t value_t::annotated_date() const
|
value_t value_t::annotated_date() const
|
||||||
|
|
@ -1321,7 +1358,7 @@ value_t value_t::annotated_date() const
|
||||||
}
|
}
|
||||||
|
|
||||||
throw_(value_error, "Cannot find the annotated date of " << label());
|
throw_(value_error, "Cannot find the annotated date of " << label());
|
||||||
return value_t();
|
return NULL_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
value_t value_t::annotated_tag() const
|
value_t value_t::annotated_tag() const
|
||||||
|
|
@ -1342,7 +1379,7 @@ value_t value_t::annotated_tag() const
|
||||||
}
|
}
|
||||||
|
|
||||||
throw_(value_error, "Cannot find the annotated tag of " << label());
|
throw_(value_error, "Cannot find the annotated tag of " << label());
|
||||||
return value_t();
|
return NULL_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
value_t value_t::strip_annotations(const bool keep_price,
|
value_t value_t::strip_annotations(const bool keep_price,
|
||||||
|
|
@ -1378,7 +1415,7 @@ value_t value_t::strip_annotations(const bool keep_price,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
assert(false);
|
assert(false);
|
||||||
return value_t();
|
return NULL_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
value_t value_t::cost() const
|
value_t value_t::cost() const
|
||||||
|
|
@ -1401,7 +1438,7 @@ value_t value_t::cost() const
|
||||||
}
|
}
|
||||||
|
|
||||||
throw_(value_error, "Cannot find the cost of " << label());
|
throw_(value_error, "Cannot find the cost of " << label());
|
||||||
return value_t();
|
return NULL_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
value_t& value_t::add(const amount_t& amount, const optional<amount_t>& tcost)
|
value_t& value_t::add(const amount_t& amount, const optional<amount_t>& tcost)
|
||||||
|
|
|
||||||
1
value.h
1
value.h
|
|
@ -400,6 +400,7 @@ public:
|
||||||
operator bool() const;
|
operator bool() const;
|
||||||
|
|
||||||
bool is_realzero() const;
|
bool is_realzero() const;
|
||||||
|
bool is_zero() const;
|
||||||
bool is_null() const {
|
bool is_null() const {
|
||||||
if (! storage) {
|
if (! storage) {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue