diff options
-rw-r--r-- | amount.cc | 19 | ||||
-rw-r--r-- | amount.h | 17 | ||||
-rw-r--r-- | commodity.cc | 69 | ||||
-rw-r--r-- | commodity.h | 12 | ||||
-rw-r--r-- | journal.cc | 348 | ||||
-rw-r--r-- | journal.h | 10 | ||||
-rw-r--r-- | main.cc | 5 | ||||
-rw-r--r-- | report.cc | 2 | ||||
-rw-r--r-- | valexpr.cc | 4 | ||||
-rw-r--r-- | value.cc | 73 | ||||
-rw-r--r-- | value.h | 1 |
11 files changed, 379 insertions, 181 deletions
@@ -855,19 +855,18 @@ bool amount_t::commodity_annotated() const return commodity().annotated; } -annotation_t amount_t::annotation_details() const +annotation_t& amount_t::annotation_details() { if (! quantity) throw_(amount_error, "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())); - return ann_comm.details; - } - return annotation_t(); + annotated_commodity_t& ann_comm(as_annotated_commodity(commodity())); + return ann_comm.details; } 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()); - if (! quantity) - throw_(amount_error, "Cannot write out an uninitialized amount"); + if (! quantity) { + _out << "<null>"; + return; + } amount_t base(*this); if (! amount_t::keep_base) @@ -538,12 +538,17 @@ public: * amount_t::keep_price, amount_t::keep_date and amount_t::keep_tag * have been set to (which all default to false). */ - void annotate_commodity(const annotation_t& details); - bool commodity_annotated() const; - annotation_t annotation_details() const; - amount_t strip_annotations(const bool _keep_price = keep_price, - const bool _keep_date = keep_date, - const bool _keep_tag = keep_tag) const; + void annotate_commodity(const annotation_t& details); + bool commodity_annotated() const; + + annotation_t& annotation_details(); + const annotation_t& annotation_details() 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 diff --git a/commodity.cc b/commodity.cc index 3bb3ec72..37c4fab9 100644 --- a/commodity.cc +++ b/commodity.cc @@ -118,6 +118,75 @@ 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) +{ + // (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 { return this != parent().null_commodity; diff --git a/commodity.h b/commodity.h index 5a8df20b..7bac7c18 100644 --- a/commodity.h +++ b/commodity.h @@ -117,6 +117,10 @@ public: operator bool() const; + bool is_annotated() const { + return annotated; + } + virtual bool operator==(const commodity_t& comm) const { if (comm.annotated) return comm == *this; @@ -185,6 +189,14 @@ public: 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(char *& p, string& symbol); static string parse_symbol(std::istream& in) { @@ -118,185 +118,249 @@ bool entry_base_t::remove_transaction(transaction_t * xact) bool entry_base_t::finalize() { - // Scan through and compute the total balance for the entry. This - // is used for auto-calculating the value of entries with no cost, - // and the per-unit price of unpriced commodities. - - value_t balance; - bool no_amounts = true; - bool saw_null = false; + // Scan through and compute the total balance for the entry. This is used + // for auto-calculating the value of entries with no cost, and the per-unit + // price of unpriced commodities. + + // (let ((balance 0) + // null-xact) + + 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(); x != transactions.end(); x++) { - if (! (*x)->has_flags(TRANSACTION_VIRTUAL) || - (*x)->has_flags(TRANSACTION_BALANCE)) { + if ((*x)->must_balance()) { amount_t& p((*x)->cost ? *(*x)->cost : (*x)->amount); if (! p.is_null()) { - if (no_amounts) { + if (balance.is_null()) balance = p; - no_amounts = false; - } else { + else 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 { - 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()); - // If it's a null entry, then let the user have their fun - if (no_amounts) - return true; + DEBUG("ledger.journal.finalize", "initial balance = " << balance); - // If there is only one transaction, balance against the basket - // account if one has been set. + // If there is only one transaction, balance against the default account if + // 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) { - assert(balance.is_amount()); - transaction_t * nxact = new transaction_t(journal->basket); - // The amount doesn't need to be set because the code below will - // balance this transaction against the other. - add_transaction(nxact); - nxact->add_flags(TRANSACTION_CALCULATED); + // jww (2008-07-24): Need to make the rest of the code aware of what to do + // when it sees a generated transaction. + null_xact = new transaction_t(journal->basket, TRANSACTION_GENERATED); + null_xact->state = (*transactions.begin())->state; + add_transaction(null_xact); } - // If the first transaction of a two-transaction entry is of a - // different commodity than the other, and it has no per-unit price, - // determine its price by dividing the unit count into the value of - // the balance. This is done for the last eligible commodity. + if (null_xact != NULL) { + // If one transaction has no value at all, its value will become the + // inverse of the rest. If multiple commodities are involved, multiple + // 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()); - 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 = - bal.amounts.find(&this_comm); - assert(this_bal != bal.amounts.end()); - - balance_t::amounts_map::const_iterator other_bal = - bal.amounts.begin(); - - if (this_bal == other_bal) - other_bal++; - - amount_t per_unit_cost = - ((*other_bal).second / (*this_bal).second.number()).unround(); - - for (; x != transactions.end(); x++) { - if ((*x)->cost || (*x)->has_flags(TRANSACTION_VIRTUAL) || - (*x)->amount.commodity() != this_comm) - continue; - - balance -= (*x)->amount; - - entry_t * entry = dynamic_cast<entry_t *>(this); - - if ((*x)->amount.commodity() && - ! (*x)->amount.commodity().annotated) - (*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; + + balance_t::amounts_map::const_iterator a = bal.amounts.begin(); + + const amount_t& x((*a++).second); + const amount_t& y((*a++).second); + + if (! y.is_realzero()) { + amount_t per_unit_cost = (x / y).abs(); + + commodity_t& comm(x.commodity()); + + for (transactions_list::const_iterator x = transactions.begin(); + x != transactions.end(); + x++) { + const amount_t& x_amt((*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); + + (*x)->cost = per_unit_cost * x_amt; + DEBUG("ledger.journal.finalize", "*(*x)->cost = " << *(*x)->cost); + + balance += *(*x)->cost; + DEBUG("ledger.journal.finalize", "after operation 2 = " << balance); + } + } } - } - // Walk through each of the transactions, fixing up any that we - // can, and performing any on-the-fly calculations. + DEBUG("ledger.journal.finalize", "resolved balance = " << balance); + } - bool empty_allowed = true; + // Now that the transaction list has its final form, calculate the balance + // once more in terms of total cost, accounting for any possible gain/loss + // amounts. + + // (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(); x != transactions.end(); x++) { - if (! (*x)->amount.is_null() || - ((*x)->has_flags(TRANSACTION_VIRTUAL) && - ! (*x)->has_flags(TRANSACTION_BALANCE))) - continue; - - if (! empty_allowed) - 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 - // 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; - switch (balance.type()) { - case value_t::BALANCE_PAIR: - bal = &balance.as_balance_pair().quantity(); - // fall through... - - case value_t::BALANCE: - if (! bal) - bal = &balance.as_balance(); - - if (bal->amounts.size() < 2) { - balance.cast(value_t::AMOUNT); - } 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; + if ((*x)->cost) { + const amount_t& x_amt((*x)->amount); + + assert(x_amt.commodity() != (*x)->cost->commodity()); + + 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, + (*x)->cost, none, (*x)->actual_date(), + entry ? entry->code : optional<string>()); + + if ((*x)->amount.commodity_annotated()) { + if (ann_amount.annotation_details().price) { + if (balance.is_null()) + balance = basis_cost - final_cost; + else + balance += basis_cost - final_cost; } - 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 = new balance_error("Entry does not balance", new entry_context(*this, "While balancing entry:")); - DEBUG("ledger.journal.unbalanced_remainder", "balance = " << balance); balance.round(); err->context.push_front (new value_context(balance, "Unbalanced remainder is:")); @@ -46,6 +46,7 @@ namespace ledger { #define TRANSACTION_AUTO 0x0004 #define TRANSACTION_BULK_ALLOC 0x0008 #define TRANSACTION_CALCULATED 0x0010 +#define TRANSACTION_GENERATED 0x0020 class entry_t; class account_t; @@ -73,8 +74,9 @@ class transaction_t : public supports_flags<> mutable void * data; static bool use_effective_date; - transaction_t(account_t * _account = NULL) - : supports_flags<>(TRANSACTION_NORMAL), entry(NULL), + transaction_t(account_t * _account = NULL, + unsigned int _flags = TRANSACTION_NORMAL) + : supports_flags<>(_flags), entry(NULL), state(UNCLEARED), account(_account), 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(); } + bool must_balance() const { + return ! has_flags(TRANSACTION_VIRTUAL) || has_flags(TRANSACTION_BALANCE); + } + bool valid() const; }; @@ -205,12 +205,15 @@ static int read_and_report(ledger::report_t& report, int argc, char * argv[], 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; " "did you specify a valid file with -f?"); INFO_FINISH(journal); + INFO("Found " << count << " entries"); + TRACE_FINISH(entry_text, 1); TRACE_FINISH(entry_date, 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, static_cast<int>(abbrev_len)), true); #else - return value_t(); + return NULL_VALUE; #endif } @@ -87,7 +87,7 @@ value_t scope_t::resolve(const string& name) { if (definition) return definition->calc(*this); else - return value_t(); + return NULL_VALUE; } void symbol_scope_t::define(const string& name, ptr_op_t def) @@ -135,7 +135,7 @@ namespace { temp.reset(expr); } else { 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); } } else { @@ -298,7 +298,9 @@ void value_t::in_place_simplify() if (is_balance() && as_balance().amounts.size() == 1) { DEBUG_("Reducing balance to amount"); + DEBUG("ledger.value.reduce", "as a balance it looks like: " << *this); in_place_cast(AMOUNT); + DEBUG("ledger.value.reduce", "as an amount it looks like: " << *this); } #if 0 @@ -445,7 +447,7 @@ value_t& value_t::operator+=(const value_t& val) break; } - throw_(value_error, "Cannot add " << label() << " to " << val.label()); + throw_(value_error, "Cannot add " << val.label() << " to " << label()); return *this; } @@ -605,7 +607,7 @@ value_t& value_t::operator-=(const value_t& val) break; } - throw_(value_error, "Cannot subtract " << label() << " from " << val.label()); + throw_(value_error, "Cannot subtract " << val.label() << " from " << label()); return *this; } @@ -952,8 +954,7 @@ bool value_t::operator>(const value_t& val) const break; } - throw_(value_error, - "Cannot compare " << label() << " to " << val.label()); + throw_(value_error, "Cannot compare " << label() << " to " << val.label()); return *this; } @@ -1019,13 +1020,13 @@ void value_t::in_place_cast(type_t cast_type) if (amt.is_null()) set_balance(balance_t()); else - set_balance(as_amount()); + set_balance(as_amount()); // creates temporary return; case BALANCE_PAIR: if (amt.is_null()) set_balance_pair(balance_pair_t()); else - set_balance_pair(as_amount()); + set_balance_pair(as_amount()); // creates temporary return; case STRING: if (amt.is_null()) @@ -1044,7 +1045,10 @@ void value_t::in_place_cast(type_t cast_type) case AMOUNT: { const balance_t& temp(as_balance()); 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; } else if (temp.amounts.size() == 0) { @@ -1070,7 +1074,7 @@ void value_t::in_place_cast(type_t cast_type) case AMOUNT: { const balance_t& temp(as_balance_pair().quantity()); if (temp.amounts.size() == 1) { - set_amount((*temp.amounts.begin()).second); + set_amount(amount_t((*temp.amounts.begin()).second)); return; } else if (temp.amounts.size() == 0) { @@ -1084,7 +1088,9 @@ void value_t::in_place_cast(type_t cast_type) break; } 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; default: break; @@ -1176,6 +1182,37 @@ bool value_t::is_realzero() const 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 { 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()); - return value_t(); + return NULL_VALUE; } void value_t::in_place_reduce() @@ -1248,7 +1285,7 @@ value_t value_t::abs() const } throw_(value_error, "Cannot abs " << label()); - return value_t(); + return NULL_VALUE; } value_t value_t::round() const @@ -1267,7 +1304,7 @@ value_t value_t::round() const } throw_(value_error, "Cannot round " << label()); - return value_t(); + return NULL_VALUE; } value_t value_t::unround() const @@ -1282,7 +1319,7 @@ value_t value_t::unround() const } throw_(value_error, "Cannot unround " << label()); - return value_t(); + return NULL_VALUE; } 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()); - return value_t(); + return NULL_VALUE; } 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()); - return value_t(); + return NULL_VALUE; } 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()); - return value_t(); + return NULL_VALUE; } 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; } assert(false); - return value_t(); + return NULL_VALUE; } 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()); - return value_t(); + return NULL_VALUE; } value_t& value_t::add(const amount_t& amount, const optional<amount_t>& tcost) @@ -400,6 +400,7 @@ public: operator bool() const; bool is_realzero() const; + bool is_zero() const; bool is_null() const { if (! storage) { return true; |