diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/amount.cc | 23 | ||||
-rw-r--r-- | src/commodity.cc | 184 | ||||
-rw-r--r-- | src/entry.cc | 103 | ||||
-rw-r--r-- | src/report.cc | 10 | ||||
-rw-r--r-- | src/report.h | 4 | ||||
-rw-r--r-- | src/textual.cc | 10 |
6 files changed, 183 insertions, 151 deletions
diff --git a/src/amount.cc b/src/amount.cc index 84e77f41..dde1f96c 100644 --- a/src/amount.cc +++ b/src/amount.cc @@ -515,12 +515,25 @@ amount_t::value(const bool primary_only, const optional<commodity_t&>& in_terms_of) const { if (quantity) { +#if defined(DEBUG_ON) + DEBUG("commodity.prices.find", + "amount_t::value of " << commodity().symbol()); + if (moment) + DEBUG("commodity.prices.find", + "amount_t::value: moment = " << *moment); + if (in_terms_of) + DEBUG("commodity.prices.find", + "amount_t::value: in_terms_of = " << in_terms_of->symbol()); +#endif if (has_commodity() && - (! primary_only || commodity().has_flags(COMMODITY_PRIMARY)) && - (! in_terms_of || commodity() != *in_terms_of)) { - optional<price_point_t> point(commodity().find_price(in_terms_of, moment)); - if (point) + (! primary_only || ! commodity().has_flags(COMMODITY_PRIMARY))) { + if (in_terms_of && commodity() == *in_terms_of) { + return *this; + } + else if (optional<price_point_t> point = + commodity().find_price(in_terms_of, moment)) { return (point->price * number()).rounded(); + } } } else { throw_(amount_error, "Cannot determine value of an uninitialized amount"); @@ -996,7 +1009,7 @@ void amount_t::print(std::ostream& _out) const if (comm.annotated) { annotated_commodity_t& ann(static_cast<annotated_commodity_t&>(comm)); - assert(&*ann.details.price != this); + assert(! ann.details.price || &*ann.details.price != this); ann.write_annotations(out); } diff --git a/src/commodity.cc b/src/commodity.cc index f758d3b4..5832e2b8 100644 --- a/src/commodity.cc +++ b/src/commodity.cc @@ -38,8 +38,9 @@ void commodity_t::base_t::history_t::add_price(commodity_t& source, const amount_t& price, const bool reflexive) { - DEBUG("commodity.prices", - "add_price to " << source << " : " << date << ", " << price); + DEBUG("commodity.prices.add", "add_price to " << source + << (reflexive ? " (secondary)" : " (primary)") + << " : " << date << ", " << price); history_map::iterator i = prices.find(date); if (i != prices.end()) { @@ -51,18 +52,19 @@ void commodity_t::base_t::history_t::add_price(commodity_t& source, } if (reflexive) { - if (! price.commodity().has_flags(COMMODITY_NOMARKET)) { - amount_t inverse = price.inverted(); - inverse.set_commodity(const_cast<commodity_t&>(source)); - price.commodity().add_price(date, inverse, false); - } + amount_t inverse = price.inverted(); + inverse.set_commodity(const_cast<commodity_t&>(source)); + price.commodity().add_price(date, inverse, false); + } else { + DEBUG("commodity.prices.add", + "marking commodity " << source.symbol() << " as primary"); source.add_flags(COMMODITY_PRIMARY); } } bool commodity_t::base_t::history_t::remove_price(const datetime_t& date) { - DEBUG("commodity.prices", "remove_price: " << date); + DEBUG("commodity.prices.add", "remove_price: " << date); history_map::size_type n = prices.erase(date); if (n > 0) @@ -93,7 +95,7 @@ void commodity_t::base_t::varied_history_t:: bool commodity_t::base_t::varied_history_t::remove_price(const datetime_t& date, commodity_t& comm) { - DEBUG("commodity.prices", "varied_remove_price: " << date << ", " << comm); + DEBUG("commodity.prices.add", "varied_remove_price: " << date << ", " << comm); if (optional<history_t&> hist = history(comm)) return hist->remove_price(date); @@ -120,22 +122,22 @@ optional<price_point_t> } while (false) #if defined(DEBUG_ON) - DEBUG_INDENT("commodity.prices", indent); + DEBUG_INDENT("commodity.prices.find", indent); if (moment) - DEBUG("commodity.prices", "find price nearest before or on: " << *moment); + DEBUG("commodity.prices.find", "find price nearest before or on: " << *moment); else - DEBUG("commodity.prices", "find any price"); + DEBUG("commodity.prices.find", "find any price"); if (oldest) { - DEBUG_INDENT("commodity.prices", indent); - DEBUG("commodity.prices", " but no older than: " << *oldest); + DEBUG_INDENT("commodity.prices.find", indent); + DEBUG("commodity.prices.find", " but no older than: " << *oldest); } #endif if (prices.size() == 0) { #if defined(DEBUG_ON) - DEBUG_INDENT("commodity.prices", indent); - DEBUG("commodity.prices", " there are no prices in this history"); + DEBUG_INDENT("commodity.prices.find", indent); + DEBUG("commodity.prices.find", " there are no prices in this history"); #endif return none; } @@ -146,8 +148,8 @@ optional<price_point_t> point.price = (*r).second; found = true; #if defined(DEBUG_ON) - DEBUG_INDENT("commodity.prices", indent); - DEBUG("commodity.prices", " using most recent price"); + DEBUG_INDENT("commodity.prices.find", indent); + DEBUG("commodity.prices.find", " using most recent price"); #endif } else { history_map::const_iterator i = prices.lower_bound(*moment); @@ -157,8 +159,8 @@ optional<price_point_t> point.price = (*r).second; found = true; #if defined(DEBUG_ON) - DEBUG_INDENT("commodity.prices", indent); - DEBUG("commodity.prices", " using last price"); + DEBUG_INDENT("commodity.prices.find", indent); + DEBUG("commodity.prices.find", " using last price"); #endif } else { point.when = (*i).first; @@ -174,8 +176,8 @@ optional<price_point_t> found = true; } #if defined(DEBUG_ON) - DEBUG_INDENT("commodity.prices", indent); - DEBUG("commodity.prices", " using found price"); + DEBUG_INDENT("commodity.prices.find", indent); + DEBUG("commodity.prices.find", " using found price"); #endif } } @@ -192,29 +194,29 @@ optional<price_point_t> if (! found) { #if defined(DEBUG_ON) - DEBUG_INDENT("commodity.prices", indent); - DEBUG("commodity.prices", " could not find a price"); + DEBUG_INDENT("commodity.prices.find", indent); + DEBUG("commodity.prices.find", " could not find a price"); #endif return none; } else if (moment && point.when > *moment) { #if defined(DEBUG_ON) - DEBUG_INDENT("commodity.prices", indent); - DEBUG("commodity.prices", " price is too young "); + DEBUG_INDENT("commodity.prices.find", indent); + DEBUG("commodity.prices.find", " price is too young "); #endif return none; } else if (oldest && point.when < *oldest) { #if defined(DEBUG_ON) - DEBUG_INDENT("commodity.prices", indent); - DEBUG("commodity.prices", " price is too old "); + DEBUG_INDENT("commodity.prices.find", indent); + DEBUG("commodity.prices.find", " price is too old "); #endif return none; } else { #if defined(DEBUG_ON) - DEBUG_INDENT("commodity.prices", indent); - DEBUG("commodity.prices", + DEBUG_INDENT("commodity.prices.find", indent); + DEBUG("commodity.prices.find", " returning price: " << point.when << ", " << point.price); #endif return point; @@ -238,23 +240,23 @@ optional<price_point_t> assert(! commodity || source != *commodity); #if defined(DEBUG_ON) - DEBUG_INDENT("commodity.prices", indent); - DEBUG("commodity.prices", "varied_find_price for: " << source); + DEBUG_INDENT("commodity.prices.find", indent); + DEBUG("commodity.prices.find", "varied_find_price for: " << source); - DEBUG_INDENT("commodity.prices", indent); + DEBUG_INDENT("commodity.prices.find", indent); if (commodity) - DEBUG("commodity.prices", " looking for: commodity '" << *commodity << "'"); + DEBUG("commodity.prices.find", " looking for: commodity '" << *commodity << "'"); else - DEBUG("commodity.prices", " looking for: any commodity"); + DEBUG("commodity.prices.find", " looking for: any commodity"); if (moment) { - DEBUG_INDENT("commodity.prices", indent); - DEBUG("commodity.prices", " time index: " << *moment); + DEBUG_INDENT("commodity.prices.find", indent); + DEBUG("commodity.prices.find", " time index: " << *moment); } if (oldest) { - DEBUG_INDENT("commodity.prices", indent); - DEBUG("commodity.prices", " only consider prices younger than: " << *oldest); + DEBUG_INDENT("commodity.prices.find", indent); + DEBUG("commodity.prices.find", " only consider prices younger than: " << *oldest); } #endif @@ -271,8 +273,8 @@ optional<price_point_t> continue; #if defined(DEBUG_ON) - DEBUG_INDENT("commodity.prices", indent + 1); - DEBUG("commodity.prices", + DEBUG_INDENT("commodity.prices.find", indent + 1); + DEBUG("commodity.prices.find", " searching for price via commodity '" << comm << "'"); #endif @@ -288,8 +290,8 @@ optional<price_point_t> if (commodity && comm != *commodity) { #if defined(DEBUG_ON) - DEBUG_INDENT("commodity.prices", indent + 1); - DEBUG("commodity.prices", " looking for translation price"); + DEBUG_INDENT("commodity.prices.find", indent + 1); + DEBUG("commodity.prices.find", " looking for translation price"); #endif xlat = comm.find_price(commodity, moment, limit @@ -299,8 +301,8 @@ optional<price_point_t> ); if (xlat) { #if defined(DEBUG_ON) - DEBUG_INDENT("commodity.prices", indent + 1); - DEBUG("commodity.prices", " found translated price " + DEBUG_INDENT("commodity.prices.find", indent + 1); + DEBUG("commodity.prices.find", " found translated price " << xlat->price << " from " << xlat->when); #endif @@ -308,15 +310,15 @@ optional<price_point_t> if (xlat->when < point->when) { point->when = xlat->when; #if defined(DEBUG_ON) - DEBUG_INDENT("commodity.prices", indent + 1); - DEBUG("commodity.prices", + DEBUG_INDENT("commodity.prices.find", indent + 1); + DEBUG("commodity.prices.find", " adjusting date of result back to " << point->when); #endif } } else { #if defined(DEBUG_ON) - DEBUG_INDENT("commodity.prices", indent + 1); - DEBUG("commodity.prices", " saw no translated price there"); + DEBUG_INDENT("commodity.prices.find", indent + 1); + DEBUG("commodity.prices.find", " saw no translated price there"); #endif continue; } @@ -324,8 +326,8 @@ optional<price_point_t> assert(! commodity || point->price.commodity() == *commodity); #if defined(DEBUG_ON) - DEBUG_INDENT("commodity.prices", indent + 1); - DEBUG("commodity.prices", + DEBUG_INDENT("commodity.prices.find", indent + 1); + DEBUG("commodity.prices.find", " saw a price there: " << point->price << " from " << point->when); #endif if (! limit || point->when > *limit) { @@ -335,16 +337,16 @@ optional<price_point_t> } } else { #if defined(DEBUG_ON) - DEBUG_INDENT("commodity.prices", indent + 1); - DEBUG("commodity.prices", " saw no price there"); + DEBUG_INDENT("commodity.prices.find", indent + 1); + DEBUG("commodity.prices.find", " saw no price there"); #endif } } if (found) { #if defined(DEBUG_ON) - DEBUG_INDENT("commodity.prices", indent); - DEBUG("commodity.prices", + DEBUG_INDENT("commodity.prices.find", indent); + DEBUG("commodity.prices.find", " found price " << best.price << " from " << best.when); #endif return best; @@ -381,16 +383,14 @@ void commodity_t::exchange(commodity_t& commodity, const amount_t& per_unit_cost, const datetime_t& moment) { - if (! commodity.has_flags(COMMODITY_NOMARKET)) { - DEBUG("commodity.prices", "exchanging commodity " << commodity - << " at per unit cost " << per_unit_cost << " on " << 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); + commodity_t& base_commodity + (commodity.annotated ? + as_annotated_commodity(commodity).referent() : commodity); - base_commodity.add_price(moment, per_unit_cost); - } + base_commodity.add_price(moment, per_unit_cost); } commodity_t::cost_breakdown_t @@ -400,17 +400,12 @@ commodity_t::exchange(const amount_t& amount, const optional<datetime_t>& moment, const optional<string>& tag) { - // (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)))) + DEBUG("commodity.prices.add", "exchange: " << amount << " for " << cost); + DEBUG("commodity.prices.add", "exchange: is-per-unit = " << is_per_unit); + if (moment) + DEBUG("commodity.prices.add", "exchange: moment = " << *moment); + if (tag) + DEBUG("commodity.prices.add", "exchange: tag = " << *tag); commodity_t& commodity(amount.commodity()); @@ -418,44 +413,33 @@ commodity_t::exchange(const amount_t& amount, 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(is_per_unit ? cost : cost / amount); - - cost_breakdown_t breakdown; - breakdown.final_cost = ! is_per_unit ? cost : cost * amount; + amount_t per_unit_cost(is_per_unit ? cost : (cost / amount).unrounded()); - // Add a price history entry for this conversion if we know when it took - // place + DEBUG("commodity.prices.add", "exchange: per-unit-cost = " << per_unit_cost); - // (if (and moment (not (commodity-no-market-price-p base-commodity))) - // (add-price base-commodity per-unit-cost moment)) + exchange(commodity, per_unit_cost, moment ? *moment : CURRENT_TIME()); - if (moment && ! commodity.has_flags(COMMODITY_NOMARKET)) - base_commodity.add_price(*moment, per_unit_cost); + cost_breakdown_t breakdown; + breakdown.final_cost = ! is_per_unit ? cost : (cost * amount).unrounded(); - // ;; 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)))) + DEBUG("commodity.prices.add", + "exchange: final-cost = " << breakdown.final_cost); if (current_annotation && current_annotation->price) - breakdown.basis_cost = *current_annotation->price * amount; + 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); + breakdown.amount = amount_t(amount, annotation_t(per_unit_cost, moment ? moment->date() : optional<date_t>(), tag)); + DEBUG("commodity.prices.add", + "exchange: amount = " << breakdown.amount); + return breakdown; } @@ -755,7 +739,7 @@ void annotated_commodity_t::write_annotations(std::ostream& out, out << " {" << *info.price << '}'; if (info.date) - out << " [" << format_date(*info.date) << ']'; + out << " [" << format_date(*info.date, string("%Y/%m/%d")) << ']'; if (info.tag) out << " (" << *info.tag << ')'; diff --git a/src/entry.cc b/src/entry.cc index a07481f1..eceec5f7 100644 --- a/src/entry.cc +++ b/src/entry.cc @@ -85,9 +85,6 @@ bool entry_base_t::finalize() // 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; xact_t * null_xact = NULL; @@ -119,8 +116,8 @@ bool entry_base_t::finalize() DEBUG("entry.finalize", "initial balance = " << balance); - // If there is only one xact, balance against the default account if - // one has been set. + // If there is only one xact, balance against the default account if one has + // been set. 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 @@ -131,9 +128,9 @@ bool entry_base_t::finalize() } if (null_xact != NULL) { - // If one xact has no value at all, its value will become the - // inverse of the rest. If multiple commodities are involved, multiple - // xacts are generated to balance them all. + // If one xact has no value at all, its value will become the inverse of + // the rest. If multiple commodities are involved, multiple xacts are + // generated to balance them all. if (balance.is_balance()) { bool first = true; @@ -161,41 +158,88 @@ bool entry_base_t::finalize() else if (balance.is_balance() && balance.as_balance().amounts.size() == 2) { // When an entry involves two different commodities (regardless of how - // many xacts 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 xact for both - // commodities. + // many xacts 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 xact for both commodities. + + DEBUG("entry.finalize", "there were exactly two commodities"); + + bool saw_cost = false; + xact_t * top_xact = NULL; + + foreach (xact_t * xact, xacts) { + if (! xact->amount.is_null()) + if (xact->amount.is_annotated()) + top_xact = xact; + else if (! top_xact) + top_xact = xact; + + if (xact->cost) { + saw_cost = true; + break; + } + } + + if (! saw_cost && top_xact) { + const balance_t& bal(balance.as_balance()); - const balance_t& bal(balance.as_balance()); + DEBUG("entry.finalize", "there were no costs, and a valid top_xact"); - balance_t::amounts_map::const_iterator a = bal.amounts.begin(); + balance_t::amounts_map::const_iterator a = bal.amounts.begin(); - const amount_t& x((*a++).second); - const amount_t& y((*a++).second); + const amount_t * x = &(*a++).second; + const amount_t * y = &(*a++).second; - if (! y.is_realzero()) { - amount_t per_unit_cost = (x / y).abs(); + if (x->commodity() != top_xact->amount.commodity()) { + const amount_t * t = x; + x = y; + y = t; + } + + DEBUG("entry.finalize", "primary amount = " << *y); + DEBUG("entry.finalize", "secondary amount = " << *x); - commodity_t& comm(x.commodity()); + commodity_t& comm(x->commodity()); + amount_t per_unit_cost; + amount_t total_cost; + + foreach (xact_t * xact, xacts) { + if (xact != top_xact && xact->must_balance() && + ! xact->amount.is_null() && + xact->amount.is_annotated() && + xact->amount.annotation().price) { + amount_t temp = *xact->amount.annotation().price * xact->amount; + if (total_cost.is_null()) { + total_cost = temp; + y = &total_cost; + } else { + total_cost += temp; + } + DEBUG("entry.finalize", "total_cost = " << total_cost); + } + } + per_unit_cost = (*y / *x).abs(); + + DEBUG("entry.finalize", "per_unit_cost = " << per_unit_cost); foreach (xact_t * xact, xacts) { const amount_t& amt(xact->amount); - if (! (xact->cost || ! xact->must_balance() || - amt.commodity() == comm)) { + if (xact->must_balance() && amt.commodity() == comm) { balance -= amt; xact->cost = per_unit_cost * amt; balance += *xact->cost; - } + DEBUG("entry.finalize", "set xact->cost to = " << *xact->cost); + } } } DEBUG("entry.finalize", "resolved balance = " << balance); } - // Now that the xact list has its final form, calculate the balance - // once more in terms of total cost, accounting for any possible gain/loss + // Now that the xact list has its final form, calculate the balance once + // more in terms of total cost, accounting for any possible gain/loss // amounts. foreach (xact_t * xact, xacts) { @@ -204,9 +248,12 @@ bool entry_base_t::finalize() throw_(balance_error, "Transaction's cost must be of a different commodity"); commodity_t::cost_breakdown_t breakdown = - commodity_t::exchange(xact->amount, *xact->cost); + commodity_t::exchange(xact->amount, *xact->cost, false, + datetime_t(date(), time_duration(0, 0, 0, 0))); - if (xact->amount.is_annotated()) + if (xact->amount.is_annotated() && + breakdown.basis_cost.commodity() == + breakdown.final_cost.commodity()) add_or_set_value(balance, (breakdown.basis_cost - breakdown.final_cost).rounded()); else @@ -231,8 +278,8 @@ bool entry_base_t::finalize() if (! xact->amount.is_null()) { all_null = false; - // jww (2008-08-09): For now, this feature only works for - // non-specific commodities. + // jww (2008-08-09): For now, this feature only works for non-specific + // commodities. add_or_set_value(xact->account->xdata().value, xact->amount); DEBUG("entry.finalize.totals", diff --git a/src/report.cc b/src/report.cc index 03b35b71..22ed926c 100644 --- a/src/report.cc +++ b/src/report.cc @@ -128,26 +128,22 @@ value_t report_t::fn_market_value(call_scope_t& scope) scoped_array<char> buf(new char[args.get<string>(2).length() + 1]); std::strcpy(buf.get(), args.get<string>(2).c_str()); - bool primary_only = false; for (char * p = std::strtok(buf.get(), ","); p; p = std::strtok(NULL, ",")) { if (commodity_t * commodity = amount_t::current_pool->find(trim_ws(p))) { value_t result = - args.value_at(0).value(primary_only, args.has(1) ? + args.value_at(0).value(false, args.has(1) ? args.get<datetime_t>(1) : optional<datetime_t>(), *commodity); if (! result.is_null()) return result; } - // For subsequent, secondary commodities, don't convert primaries - primary_only = true; } } else { value_t result = - args.value_at(0).value(! args.has(2), args.has(1) ? - args.get<datetime_t>(1) : - optional<datetime_t>()); + args.value_at(0).value(true, args.has(1) ? + args.get<datetime_t>(1) : optional<datetime_t>()); if (! result.is_null()) return result; } diff --git a/src/report.h b/src/report.h index 29f37e9d..5494db7f 100644 --- a/src/report.h +++ b/src/report.h @@ -411,9 +411,9 @@ public: OPTION_(report_t, market, DO() { // -V parent->HANDLER(revalued).on_only(); parent->HANDLER(display_amount_) - .set_expr("market(amount_expr, now, exchange)"); + .set_expr("market(amount_expr, date, exchange)"); parent->HANDLER(display_total_) - .set_expr("market(total_expr, now, exchange)"); + .set_expr("market(total_expr, date, exchange)"); }); OPTION_(report_t, monthly, DO() { // -M diff --git a/src/textual.cc b/src/textual.cc index b3fb2f2e..dec2dc3a 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -492,7 +492,7 @@ void instance_t::price_entry_directive(char * line) if (commodity_t * commodity = amount_t::current_pool->find_or_create(symbol)) { - commodity->add_price(datetime, price); + commodity->add_price(datetime, price, true); commodity->add_flags(COMMODITY_KNOWN); } else { assert(false); @@ -895,20 +895,12 @@ xact_t * instance_t::parse_xact(char * line, if (xact->cost->sign() < 0) throw parse_error("A transaction's cost may not be negative"); - amount_t per_unit_cost(*xact->cost); if (per_unit) *xact->cost *= xact->amount; - else - per_unit_cost /= xact->amount; - - commodity_t::exchange(xact->amount.commodity(), - per_unit_cost, datetime_t(xact->date())); DEBUG("textual.parse", "line " << linenum << ": " << "Total cost is " << *xact->cost); DEBUG("textual.parse", "line " << linenum << ": " - << "Per-unit cost is " << per_unit_cost); - DEBUG("textual.parse", "line " << linenum << ": " << "Annotated amount is " << xact->amount); if (cstream.eof()) |