Fixed an issue involving costs and reduced values

This commit is contained in:
John Wiegley 2009-02-24 13:19:00 -04:00
parent d525db35d8
commit 38dd1b8655
3 changed files with 91 additions and 64 deletions

View file

@ -828,9 +828,10 @@ post_t * instance_t::parse_post(char * line,
ptristream stream(next, len - beg); ptristream stream(next, len - beg);
if (*next != '(') // indicates a value expression if (*next != '(') // indicates a value expression
post->amount.parse(stream); post->amount.parse(stream, amount_t::PARSE_NO_REDUCE);
else else
parse_amount_expr(session_scope, stream, post->amount, post.get(), parse_amount_expr(session_scope, stream, post->amount, post.get(),
static_cast<uint_least8_t>(expr_t::PARSE_NO_REDUCE) |
static_cast<uint_least8_t>(expr_t::PARSE_NO_ASSIGN)); static_cast<uint_least8_t>(expr_t::PARSE_NO_ASSIGN));
if (! post->amount.is_null() && honor_strict && strict && if (! post->amount.is_null() && honor_strict && strict &&

View file

@ -89,37 +89,40 @@ bool xact_base_t::finalize()
post_t * null_post = NULL; post_t * null_post = NULL;
foreach (post_t * post, posts) { foreach (post_t * post, posts) {
if (post->must_balance()) { if (! post->must_balance())
amount_t& p(post->cost ? *post->cost : post->amount); continue;
DEBUG("xact.finalize", "post must balance = " << p);
if (! p.is_null()) { amount_t& p(post->cost ? *post->cost : post->amount);
if (! post->cost && post->amount.is_annotated() && DEBUG("xact.finalize", "post must balance = " << p.reduced());
post->amount.annotation().price) { if (! p.is_null()) {
// If the amount has no cost, but is annotated with a per-unit if (! post->cost && post->amount.is_annotated() &&
// price, use the price times the amount as the cost post->amount.annotation().price) {
post->cost = *post->amount.annotation().price * post->amount; // If the amount has no cost, but is annotated with a per-unit
DEBUG("xact.finalize", // price, use the price times the amount as the cost
"annotation price = " << *post->amount.annotation().price); post->cost = *post->amount.annotation().price * post->amount;
DEBUG("xact.finalize", "amount = " << post->amount); DEBUG("xact.finalize",
DEBUG("xact.finalize", "priced cost = " << *post->cost); "annotation price = " << *post->amount.annotation().price);
post->add_flags(POST_PRICED); DEBUG("xact.finalize", "amount = " << post->amount);
add_or_set_value(balance, post->cost->rounded()); DEBUG("xact.finalize", "priced cost = " << *post->cost);
} else { post->add_flags(POST_PRICED);
// If the amount was a cost, it very likely has the "keep_precision" add_or_set_value(balance, post->cost->rounded().reduced());
// flag set, meaning commodity display precision is ignored when
// displaying the amount. We never want this set for the balance,
// so we must clear the flag in a temporary to avoid it propagating
// into the balance.
add_or_set_value(balance, p.keep_precision() ? p.rounded() : p);
}
} else { } else {
if (null_post) // If the amount was a cost, it very likely has the "keep_precision"
throw_(std::logic_error, // flag set, meaning commodity display precision is ignored when
"Only one posting with null amount allowed per transaction"); // displaying the amount. We never want this set for the balance,
else // so we must clear the flag in a temporary to avoid it propagating
null_post = post; // into the balance.
add_or_set_value(balance, p.keep_precision() ?
p.rounded().reduced() : p.reduced());
} }
} }
else if (null_post) {
throw_(std::logic_error,
_("Only one posting with null amount allowed per transaction"));
}
else {
null_post = post;
}
} }
assert(balance.valid()); assert(balance.valid());
@ -213,42 +216,44 @@ bool xact_base_t::finalize()
y = t; y = t;
} }
DEBUG("xact.finalize", "primary amount = " << *x); if (*x && *y) {
DEBUG("xact.finalize", "secondary amount = " << *y); DEBUG("xact.finalize", "primary amount = " << *x);
DEBUG("xact.finalize", "secondary amount = " << *y);
commodity_t& comm(x->commodity()); commodity_t& comm(x->commodity());
amount_t per_unit_cost; amount_t per_unit_cost;
amount_t total_cost; amount_t total_cost;
foreach (post_t * post, posts) { foreach (post_t * post, posts) {
if (post != top_post && post->must_balance() && if (post != top_post && post->must_balance() &&
! post->amount.is_null() && ! post->amount.is_null() &&
post->amount.is_annotated() && post->amount.is_annotated() &&
post->amount.annotation().price) { post->amount.annotation().price) {
amount_t temp = *post->amount.annotation().price * post->amount; amount_t temp = *post->amount.annotation().price * post->amount;
if (total_cost.is_null()) { if (total_cost.is_null()) {
total_cost = temp; total_cost = temp;
y = &total_cost; y = &total_cost;
} else { } else {
total_cost += temp; total_cost += temp;
}
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();
DEBUG("xact.finalize", "per_unit_cost = " << per_unit_cost); DEBUG("xact.finalize", "per_unit_cost = " << per_unit_cost);
foreach (post_t * post, posts) { foreach (post_t * post, posts) {
const amount_t& amt(post->amount); const amount_t& amt(post->amount);
if (post->must_balance() && amt.commodity() == comm) { if (post->must_balance() && amt.commodity() == comm) {
balance -= amt; balance -= amt;
post->cost = per_unit_cost * amt; post->cost = per_unit_cost * amt;
post->add_flags(POST_PRICED); post->add_flags(POST_PRICED);
balance += *post->cost; balance += *post->cost;
DEBUG("xact.finalize", "set post->cost to = " << *post->cost); DEBUG("xact.finalize", "set post->cost to = " << *post->cost);
}
} }
} }
} }
@ -279,7 +284,7 @@ bool xact_base_t::finalize()
breakdown.final_cost).rounded()) { breakdown.final_cost).rounded()) {
DEBUG("xact.finalize", "gain_loss = " << gain_loss); DEBUG("xact.finalize", "gain_loss = " << gain_loss);
add_or_set_value(balance, gain_loss); add_or_set_value(balance, gain_loss.reduced());
account_t * account; account_t * account;
if (gain_loss.sign() > 0) if (gain_loss.sign() > 0)
@ -308,22 +313,28 @@ bool xact_base_t::finalize()
// Add the final calculated totals each to their related account // Add the final calculated totals each to their related account
if (dynamic_cast<xact_t *>(this)) { if (dynamic_cast<xact_t *>(this)) {
bool all_null = true; bool all_null = true;
bool some_null = false;
foreach (post_t * post, posts) { foreach (post_t * post, posts) {
if (! post->amount.is_null()) { if (! post->amount.is_null()) {
all_null = false; all_null = false;
// jww (2008-08-09): For now, this feature only works for non-specific post->amount.in_place_reduce();
// commodities.
add_or_set_value(post->account->xdata().value, post->amount); add_or_set_value(post->account->xdata().value, post->amount);
DEBUG("xact.finalize.totals", DEBUG("xact.finalize.totals",
"Total for " << post->account->fullname() << " + " "Total for " << post->account->fullname() << " + "
<< post->amount << ": " << post->account->xdata().value); << post->amount << ": " << post->account->xdata().value);
} else {
some_null = true;
} }
} }
if (all_null) if (all_null)
return false; // ignore this xact completely return false; // ignore this xact completely
else if (some_null)
throw_(balance_error,
"There cannot be null amounts after balancing a transaction");
} }
return true; return true;
@ -403,9 +414,8 @@ void auto_xact_t::extend_xact(xact_base_t& xact, bool post_handler)
amount_t amt; amount_t amt;
assert(post->amount); assert(post->amount);
if (! post->amount.commodity()) { if (! post->amount.commodity()) {
if (post_handler) if (post_handler || initial_post->amount.is_null())
continue; continue;
assert(initial_post->amount);
amt = initial_post->amount * post->amount; amt = initial_post->amount * post->amount;
} else { } else {
if (post_handler) if (post_handler)

View file

@ -4,10 +4,26 @@ i 2007/03/01 23:00:00 A
o 2007/03/02 01:00:00 o 2007/03/02 01:00:00
i 2007/03/11 23:00:00 B i 2007/03/11 23:00:00 B
o 2007/03/12 01:00:00 o 2007/03/12 01:00:00
2006/05/22 * Company
Assets:Receivable $4,000.00
Income:Contracts -40h @ $100.00
2006/05/22 * Company
Assets:Receivable $4,000.00
Income:Contracts -40h {$20} @ $100.00
Income:Gains $-3,200.00
>>>1 >>>1
7200s A 7200s A
$8,000.00 Assets:Receivable
7200s B 7200s B
$3,200.00 Equity:Capital Gains
$-3,200.00
-288000s Income
-288000s Contracts
$-3,200.00 Gains
-------------------- --------------------
14400s $8,000.00
-273600s
>>>2 >>>2
=== 0 === 0