Improved the way that entries are balanced
This commit is contained in:
parent
267b2ba5d8
commit
eb364f4183
2 changed files with 58 additions and 22 deletions
|
|
@ -67,6 +67,7 @@ public:
|
||||||
#define POST_VIRTUAL 0x10 // the account was specified with (parens)
|
#define POST_VIRTUAL 0x10 // the account was specified with (parens)
|
||||||
#define POST_MUST_BALANCE 0x20 // posting must balance in the transaction
|
#define POST_MUST_BALANCE 0x20 // posting must balance in the transaction
|
||||||
#define POST_CALCULATED 0x40 // posting's amount was calculated
|
#define POST_CALCULATED 0x40 // posting's amount was calculated
|
||||||
|
#define POST_PRICED 0x80 // posting's cost was calculated
|
||||||
|
|
||||||
xact_t * xact; // only set for posts of regular xacts
|
xact_t * xact; // only set for posts of regular xacts
|
||||||
account_t * account;
|
account_t * account;
|
||||||
|
|
|
||||||
63
src/xact.cc
63
src/xact.cc
|
|
@ -93,15 +93,24 @@ bool xact_base_t::finalize()
|
||||||
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);
|
DEBUG("xact.finalize", "post must balance = " << p);
|
||||||
if (! p.is_null()) {
|
if (! p.is_null()) {
|
||||||
if (p.keep_precision()) {
|
if (! post->cost && post->amount.is_annotated() &&
|
||||||
|
post->amount.annotation().price) {
|
||||||
|
// If the amount has no cost, but is annotated with a per-unit
|
||||||
|
// price, use the price times the amount as the cost
|
||||||
|
post->cost = *post->amount.annotation().price * post->amount;
|
||||||
|
DEBUG("xact.finalize",
|
||||||
|
"annotation price = " << *post->amount.annotation().price);
|
||||||
|
DEBUG("xact.finalize", "amount = " << post->amount);
|
||||||
|
DEBUG("xact.finalize", "priced cost = " << *post->cost);
|
||||||
|
post->add_flags(POST_PRICED);
|
||||||
|
add_or_set_value(balance, post->cost->rounded());
|
||||||
|
} else {
|
||||||
// If the amount was a cost, it very likely has the "keep_precision"
|
// If the amount was a cost, it very likely has the "keep_precision"
|
||||||
// flag set, meaning commodity display precision is ignored when
|
// flag set, meaning commodity display precision is ignored when
|
||||||
// displaying the amount. We never want this set for the balance,
|
// displaying the amount. We never want this set for the balance,
|
||||||
// so we must clear the flag in a temporary to avoid it propagating
|
// so we must clear the flag in a temporary to avoid it propagating
|
||||||
// into the balance.
|
// into the balance.
|
||||||
add_or_set_value(balance, p.rounded());
|
add_or_set_value(balance, p.keep_precision() ? p.rounded() : p);
|
||||||
} else {
|
|
||||||
add_or_set_value(balance, p);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (null_post)
|
if (null_post)
|
||||||
|
|
@ -114,7 +123,13 @@ bool xact_base_t::finalize()
|
||||||
}
|
}
|
||||||
assert(balance.valid());
|
assert(balance.valid());
|
||||||
|
|
||||||
|
#if defined(DEBUG_ON)
|
||||||
DEBUG("xact.finalize", "initial balance = " << balance);
|
DEBUG("xact.finalize", "initial balance = " << balance);
|
||||||
|
DEBUG("xact.finalize", "balance is " << balance.label());
|
||||||
|
if (balance.is_balance())
|
||||||
|
DEBUG("xact.finalize", "balance commodity count = "
|
||||||
|
<< balance.as_balance().amounts.size());
|
||||||
|
#endif
|
||||||
|
|
||||||
// If there is only one post, balance against the default account if one has
|
// If there is only one post, balance against the default account if one has
|
||||||
// been set.
|
// been set.
|
||||||
|
|
@ -132,6 +147,8 @@ bool xact_base_t::finalize()
|
||||||
// the rest. If multiple commodities are involved, multiple posts are
|
// the rest. If multiple commodities are involved, multiple posts are
|
||||||
// generated to balance them all.
|
// generated to balance them all.
|
||||||
|
|
||||||
|
DEBUG("xact.finalize", "there was a null posting");
|
||||||
|
|
||||||
if (balance.is_balance()) {
|
if (balance.is_balance()) {
|
||||||
bool first = true;
|
bool first = true;
|
||||||
const balance_t& bal(balance.as_balance());
|
const balance_t& bal(balance.as_balance());
|
||||||
|
|
@ -174,7 +191,7 @@ bool xact_base_t::finalize()
|
||||||
else if (! top_post)
|
else if (! top_post)
|
||||||
top_post = post;
|
top_post = post;
|
||||||
|
|
||||||
if (post->cost) {
|
if (post->cost && ! post->has_flags(POST_PRICED)) {
|
||||||
saw_cost = true;
|
saw_cost = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -196,8 +213,8 @@ bool xact_base_t::finalize()
|
||||||
y = t;
|
y = t;
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUG("xact.finalize", "primary amount = " << *y);
|
DEBUG("xact.finalize", "primary amount = " << *x);
|
||||||
DEBUG("xact.finalize", "secondary 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;
|
||||||
|
|
@ -228,24 +245,28 @@ bool xact_base_t::finalize()
|
||||||
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);
|
||||||
balance += *post->cost;
|
balance += *post->cost;
|
||||||
|
|
||||||
DEBUG("xact.finalize", "set post->cost to = " << *post->cost);
|
DEBUG("xact.finalize", "set post->cost to = " << *post->cost);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUG("xact.finalize", "resolved balance = " << balance);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now that the post list has its final form, calculate the balance once
|
// Now that the post list has its final form, calculate the balance once
|
||||||
// more in terms of total cost, accounting for any possible gain/loss
|
// more in terms of total cost, accounting for any possible gain/loss
|
||||||
// amounts.
|
// amounts.
|
||||||
|
|
||||||
|
DEBUG("xact.finalize", "resolved balance = " << balance);
|
||||||
|
|
||||||
foreach (post_t * post, posts) {
|
foreach (post_t * post, posts) {
|
||||||
if (post->cost) {
|
if (! post->cost)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (post->amount.commodity() == post->cost->commodity())
|
if (post->amount.commodity() == post->cost->commodity())
|
||||||
throw_(balance_error, "Posting's cost must be of a different commodity");
|
throw_(balance_error,
|
||||||
|
"A posting's cost must be of a different commodity than its amount");
|
||||||
|
|
||||||
commodity_t::cost_breakdown_t breakdown =
|
commodity_t::cost_breakdown_t breakdown =
|
||||||
commodity_t::exchange(post->amount, *post->cost, false,
|
commodity_t::exchange(post->amount, *post->cost, false,
|
||||||
|
|
@ -253,11 +274,25 @@ bool xact_base_t::finalize()
|
||||||
|
|
||||||
if (post->amount.is_annotated() &&
|
if (post->amount.is_annotated() &&
|
||||||
breakdown.basis_cost.commodity() ==
|
breakdown.basis_cost.commodity() ==
|
||||||
breakdown.final_cost.commodity())
|
breakdown.final_cost.commodity()) {
|
||||||
add_or_set_value(balance, (breakdown.basis_cost -
|
if (amount_t gain_loss = (breakdown.basis_cost -
|
||||||
breakdown.final_cost).rounded());
|
breakdown.final_cost).rounded()) {
|
||||||
|
DEBUG("xact.finalize", "gain_loss = " << gain_loss);
|
||||||
|
|
||||||
|
add_or_set_value(balance, gain_loss);
|
||||||
|
|
||||||
|
account_t * account;
|
||||||
|
if (gain_loss.sign() > 0)
|
||||||
|
account = journal->find_account(_("Equity:Capital Gains"));
|
||||||
else
|
else
|
||||||
|
account = journal->find_account(_("Equity:Capital Losses"));
|
||||||
|
|
||||||
|
add_post(new post_t(account, gain_loss.rounded(), ITEM_GENERATED));
|
||||||
|
DEBUG("xact.finalize", "added gain_loss, balance = " << balance);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
post->amount = breakdown.amount;
|
post->amount = breakdown.amount;
|
||||||
|
DEBUG("xact.finalize", "added breakdown, balance = " << balance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue