Fixed bugs relating to sign and rounding of costs

This commit is contained in:
John Wiegley 2009-02-27 03:28:14 -04:00
parent 501949a364
commit f1795e628b
6 changed files with 39 additions and 15 deletions

View file

@ -413,20 +413,21 @@ commodity_t::exchange(const amount_t& amount,
if (commodity.annotated) if (commodity.annotated)
current_annotation = &as_annotated_commodity(commodity).details; current_annotation = &as_annotated_commodity(commodity).details;
amount_t per_unit_cost(is_per_unit ? cost : (cost / amount).unrounded()); amount_t per_unit_cost = (is_per_unit ? cost : cost / amount).abs();
DEBUG("commodity.prices.add", "exchange: per-unit-cost = " << per_unit_cost); DEBUG("commodity.prices.add", "exchange: per-unit-cost = " << per_unit_cost);
exchange(commodity, per_unit_cost, moment ? *moment : CURRENT_TIME()); exchange(commodity, per_unit_cost, moment ? *moment : CURRENT_TIME());
cost_breakdown_t breakdown; cost_breakdown_t breakdown;
breakdown.final_cost = ! is_per_unit ? cost : (cost * amount).unrounded(); breakdown.final_cost = ! is_per_unit ? cost : cost * amount;
DEBUG("commodity.prices.add", DEBUG("commodity.prices.add",
"exchange: final-cost = " << breakdown.final_cost); "exchange: final-cost = " << breakdown.final_cost);
if (current_annotation && current_annotation->price) if (current_annotation && current_annotation->price)
breakdown.basis_cost = (*current_annotation->price * amount).unrounded(); breakdown.basis_cost
= (*current_annotation->price * amount).unrounded();
else else
breakdown.basis_cost = breakdown.final_cost; breakdown.basis_cost = breakdown.final_cost;

View file

@ -319,9 +319,15 @@ bool post_t::valid() const
return false; return false;
} }
if (cost && ! cost->valid()) { if (cost) {
DEBUG("ledger.validate", "post_t: cost && ! cost->valid()"); if (! cost->valid()) {
return false; DEBUG("ledger.validate", "post_t: cost && ! cost->valid()");
return false;
}
if (! cost->keep_precision()) {
DEBUG("ledger.validate", "post_t: ! cost->keep_precision()");
return false;
}
} }
return true; return true;

View file

@ -203,6 +203,12 @@ value_t report_t::fn_quantity(call_scope_t& scope)
return args.get<amount_t>(0).number(); return args.get<amount_t>(0).number();
} }
value_t report_t::fn_abs(call_scope_t& scope)
{
interactive_t args(scope, "v");
return args.value_at(0).abs();
}
value_t report_t::fn_truncated(call_scope_t& scope) value_t report_t::fn_truncated(call_scope_t& scope)
{ {
interactive_t args(scope, "v&ll"); interactive_t args(scope, "v&ll");
@ -568,6 +574,8 @@ expr_t::ptr_op_t report_t::lookup(const string& name)
return MAKE_FUNCTOR(report_t::fn_amount_expr); return MAKE_FUNCTOR(report_t::fn_amount_expr);
else if (is_eq(p, "ansify_if")) else if (is_eq(p, "ansify_if"))
return MAKE_FUNCTOR(report_t::fn_ansify_if); return MAKE_FUNCTOR(report_t::fn_ansify_if);
else if (is_eq(p, "abs"))
return MAKE_FUNCTOR(report_t::fn_abs);
break; break;
case 'c': case 'c':

View file

@ -140,6 +140,7 @@ public:
value_t fn_quantity(call_scope_t& scope); value_t fn_quantity(call_scope_t& scope);
value_t fn_rounded(call_scope_t& scope); value_t fn_rounded(call_scope_t& scope);
value_t fn_truncated(call_scope_t& scope); value_t fn_truncated(call_scope_t& scope);
value_t fn_abs(call_scope_t& scope);
value_t fn_justify(call_scope_t& scope); value_t fn_justify(call_scope_t& scope);
value_t fn_quoted(call_scope_t& scope); value_t fn_quoted(call_scope_t& scope);
value_t fn_join(call_scope_t& scope); value_t fn_join(call_scope_t& scope);
@ -514,12 +515,12 @@ public:
" %(xact.uncleared ? (cleared ? \"* \" : (pending ? \"! \" : \"\")) : \"\")" " %(xact.uncleared ? (cleared ? \"* \" : (pending ? \"! \" : \"\")) : \"\")"
"%-34(account)" "%-34(account)"
" %12(calculated ? \"\" : justify(scrub(amount), 12, -1, true))" " %12(calculated ? \"\" : justify(scrub(amount), 12, -1, true))"
"%(has_cost & !priced ? \" @ \" + justify(scrub(cost / amount), 0) : \"\")" "%(has_cost & !priced ? \" @ \" + justify(scrub(abs(cost / amount)), 0) : \"\")"
"%(comment | \"\")\n%/" "%(comment | \"\")\n%/"
" %(xact.uncleared ? (cleared ? \"* \" : (pending ? \"! \" : \"\")) : \"\")" " %(xact.uncleared ? (cleared ? \"* \" : (pending ? \"! \" : \"\")) : \"\")"
"%-34(account)" "%-34(account)"
" %12(calculated ? \"\" : justify(scrub(amount), 12, -1, true))" " %12(calculated ? \"\" : justify(scrub(amount), 12, -1, true))"
"%(has_cost & !priced ? \" @ \" + justify(scrub(cost / amount), 0) : \"\")" "%(has_cost & !priced ? \" @ \" + justify(scrub(abs(cost / amount)), 0) : \"\")"
"%(comment | \"\")\n%/\n"); "%(comment | \"\")\n%/\n");
}); });

View file

@ -954,6 +954,8 @@ post_t * instance_t::parse_post(char * line,
if (post->cost->sign() < 0) if (post->cost->sign() < 0)
throw parse_error(_("A posting's cost may not be negative")); throw parse_error(_("A posting's cost may not be negative"));
post->cost->in_place_unround();
if (per_unit) if (per_unit)
*post->cost *= post->amount; *post->cost *= post->amount;

View file

@ -80,13 +80,14 @@ bool xact_base_t::finalize()
continue; continue;
amount_t& p(post->cost ? *post->cost : post->amount); amount_t& p(post->cost ? *post->cost : post->amount);
DEBUG("xact.finalize", "post must balance = " << p.reduced());
if (! p.is_null()) { if (! p.is_null()) {
DEBUG("xact.finalize", "post must balance = " << p.reduced());
if (! post->cost && post->amount.is_annotated() && if (! post->cost && post->amount.is_annotated() &&
post->amount.annotation().price) { post->amount.annotation().price) {
// If the amount has no cost, but is annotated with a per-unit // If the amount has no cost, but is annotated with a per-unit
// price, use the price times the amount as the cost // price, use the price times the amount as the cost
post->cost = *post->amount.annotation().price * post->amount; post->cost = (*post->amount.annotation().price *
post->amount).unrounded();
DEBUG("xact.finalize", DEBUG("xact.finalize",
"annotation price = " << *post->amount.annotation().price); "annotation price = " << *post->amount.annotation().price);
DEBUG("xact.finalize", "amount = " << post->amount); DEBUG("xact.finalize", "amount = " << post->amount);
@ -145,10 +146,11 @@ bool xact_base_t::finalize()
foreach (const balance_t::amounts_map::value_type& pair, bal.amounts) { foreach (const balance_t::amounts_map::value_type& pair, bal.amounts) {
if (first) { if (first) {
null_post->amount = pair.second.negated(); null_post->amount = pair.second.negated();
null_post->add_flags(POST_CALCULATED);
first = false; first = false;
} else { } else {
post_t * p = new post_t(null_post->account, pair.second.negated(), post_t * p = new post_t(null_post->account, pair.second.negated(),
ITEM_GENERATED); ITEM_GENERATED | POST_CALCULATED);
p->set_state(null_post->state()); p->set_state(null_post->state());
add_post(p); add_post(p);
} }
@ -228,7 +230,7 @@ bool xact_base_t::finalize()
DEBUG("xact.finalize", "total_cost = " << total_cost); DEBUG("xact.finalize", "total_cost = " << total_cost);
} }
} }
per_unit_cost = (*y / *x).abs(); per_unit_cost = (*y / *x).abs().unrounded();
DEBUG("xact.finalize", "per_unit_cost = " << per_unit_cost); DEBUG("xact.finalize", "per_unit_cost = " << per_unit_cost);
@ -254,7 +256,9 @@ bool xact_base_t::finalize()
DEBUG("xact.finalize", "resolved balance = " << balance); DEBUG("xact.finalize", "resolved balance = " << balance);
foreach (post_t * post, posts) { posts_list copy(posts);
foreach (post_t * post, copy) {
if (! post->cost) if (! post->cost)
continue; continue;
@ -281,7 +285,7 @@ bool xact_base_t::finalize()
else else
account = journal->find_account(_("Equity:Capital Losses")); account = journal->find_account(_("Equity:Capital Losses"));
post_t * p = new post_t(account, gain_loss.rounded(), ITEM_GENERATED); post_t * p = new post_t(account, gain_loss, ITEM_GENERATED);
p->set_state(post->state()); p->set_state(post->state());
add_post(p); add_post(p);
DEBUG("xact.finalize", "added gain_loss, balance = " << balance); DEBUG("xact.finalize", "added gain_loss, balance = " << balance);
@ -328,6 +332,8 @@ bool xact_base_t::finalize()
_("There cannot be null amounts after balancing a transaction")); _("There cannot be null amounts after balancing a transaction"));
} }
VERIFY(valid());
return true; return true;
} }