diff options
Diffstat (limited to 'src/xact.cc')
-rw-r--r-- | src/xact.cc | 468 |
1 files changed, 234 insertions, 234 deletions
diff --git a/src/xact.cc b/src/xact.cc index d943e611..b4438b48 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -101,9 +101,9 @@ value_t xact_base_t::magnitude() const foreach (const post_t * post, posts) { if (post->amount.sign() > 0) { if (post->cost) - halfbal += *post->cost; + halfbal += *post->cost; else - halfbal += post->amount; + halfbal += post->amount; } } return halfbal; @@ -126,30 +126,30 @@ bool xact_base_t::finalize() if (! p.is_null()) { DEBUG("xact.finalize", "post must balance = " << p.reduced()); if (! post->cost && post->amount.has_annotation() && - 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).unrounded(); - 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_COST_CALCULATED); - add_or_set_value(balance, post->cost->rounded().reduced()); + 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).unrounded(); + 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_COST_CALCULATED); + add_or_set_value(balance, post->cost->rounded().reduced()); } else { - // If the amount was a cost, it very likely has the "keep_precision" - // 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().reduced() : p.reduced()); + // If the amount was a cost, it very likely has the "keep_precision" + // 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().reduced() : p.reduced()); } } else if (null_post) { throw_(std::logic_error, - _("Only one posting with null amount allowed per transaction")); + _("Only one posting with null amount allowed per transaction")); } else { null_post = post; @@ -162,7 +162,7 @@ bool xact_base_t::finalize() DEBUG("xact.finalize", "balance is " << balance.label()); if (balance.is_balance()) DEBUG("xact.finalize", "balance commodity count = " - << balance.as_balance().amounts.size()); + << balance.as_balance().amounts.size()); #endif // If there is only one post, balance against the default account if one has @@ -188,15 +188,15 @@ bool xact_base_t::finalize() foreach (post_t * post, posts) { if (! post->amount.is_null() && post->must_balance()) { - if (post->amount.has_annotation()) - top_post = post; - else if (! top_post) - top_post = post; + if (post->amount.has_annotation()) + top_post = post; + else if (! top_post) + top_post = post; } if (post->cost && ! post->has_flags(POST_COST_CALCULATED)) { - saw_cost = true; - break; + saw_cost = true; + break; } } @@ -211,58 +211,58 @@ bool xact_base_t::finalize() const amount_t * y = &(*a++).second; if (x->commodity() != top_post->amount.commodity()) { - const amount_t * t = x; - x = y; - y = t; + const amount_t * t = x; + x = y; + y = t; } if (*x && *y) { - DEBUG("xact.finalize", "primary amount = " << *x); - DEBUG("xact.finalize", "secondary amount = " << *y); - - commodity_t& comm(x->commodity()); - amount_t per_unit_cost; - amount_t total_cost; - const amount_t * prev_y = y; - - foreach (post_t * post, posts) { - if (post != top_post && post->must_balance() && - ! post->amount.is_null() && - post->amount.has_annotation() && - post->amount.annotation().price) { - amount_t temp = *post->amount.annotation().price * post->amount; - if (total_cost.is_null()) { - total_cost = temp; - y = &total_cost; - } - else if (total_cost.commodity() == temp.commodity()) { - total_cost += temp; - } - else { - DEBUG("xact.finalize", - "multiple price commodities, aborting price calc"); - y = prev_y; - break; - } - DEBUG("xact.finalize", "total_cost = " << total_cost); - } - } - per_unit_cost = (*y / *x).abs().unrounded(); - - DEBUG("xact.finalize", "per_unit_cost = " << per_unit_cost); - - foreach (post_t * post, posts) { - const amount_t& amt(post->amount); - - if (post->must_balance() && amt.commodity() == comm) { - balance -= amt; - post->cost = per_unit_cost * amt; - post->add_flags(POST_COST_CALCULATED); - balance += *post->cost; - - DEBUG("xact.finalize", "set post->cost to = " << *post->cost); - } - } + DEBUG("xact.finalize", "primary amount = " << *x); + DEBUG("xact.finalize", "secondary amount = " << *y); + + commodity_t& comm(x->commodity()); + amount_t per_unit_cost; + amount_t total_cost; + const amount_t * prev_y = y; + + foreach (post_t * post, posts) { + if (post != top_post && post->must_balance() && + ! post->amount.is_null() && + post->amount.has_annotation() && + post->amount.annotation().price) { + amount_t temp = *post->amount.annotation().price * post->amount; + if (total_cost.is_null()) { + total_cost = temp; + y = &total_cost; + } + else if (total_cost.commodity() == temp.commodity()) { + total_cost += temp; + } + else { + DEBUG("xact.finalize", + "multiple price commodities, aborting price calc"); + y = prev_y; + break; + } + DEBUG("xact.finalize", "total_cost = " << total_cost); + } + } + per_unit_cost = (*y / *x).abs().unrounded(); + + DEBUG("xact.finalize", "per_unit_cost = " << per_unit_cost); + + foreach (post_t * post, posts) { + const amount_t& amt(post->amount); + + if (post->must_balance() && amt.commodity() == comm) { + balance -= amt; + post->cost = per_unit_cost * amt; + post->add_flags(POST_COST_CALCULATED); + balance += *post->cost; + + DEBUG("xact.finalize", "set post->cost to = " << *post->cost); + } + } } } } @@ -275,39 +275,39 @@ bool xact_base_t::finalize() if (post->amount.commodity() == post->cost->commodity()) throw_(balance_error, - _("A posting's cost must be of a different commodity than its amount")); + _("A posting's cost must be of a different commodity than its amount")); cost_breakdown_t breakdown = commodity_pool_t::current_pool->exchange (post->amount, *post->cost, false, - datetime_t(date(), time_duration(0, 0, 0, 0))); + datetime_t(date(), time_duration(0, 0, 0, 0))); if (post->amount.has_annotation() && - breakdown.basis_cost.commodity() == breakdown.final_cost.commodity()) { + breakdown.basis_cost.commodity() == breakdown.final_cost.commodity()) { if (amount_t gain_loss = breakdown.basis_cost - breakdown.final_cost) { - DEBUG("xact.finalize", "gain_loss = " << gain_loss); - gain_loss.in_place_round(); - DEBUG("xact.finalize", "gain_loss rounds to = " << gain_loss); - - if (post->must_balance()) - add_or_set_value(balance, gain_loss.reduced()); - - account_t * account; - if (gain_loss.sign() > 0) - account = journal->find_account(_("Equity:Capital Gains")); - else - account = journal->find_account(_("Equity:Capital Losses")); - - post_t * p = new post_t(account, gain_loss, ITEM_GENERATED); - p->set_state(post->state()); - if (post->has_flags(POST_VIRTUAL)) { - DEBUG("xact.finalize", "gain_loss came from a virtual post"); - p->add_flags(post->flags() & (POST_VIRTUAL | POST_MUST_BALANCE)); - } - add_post(p); - DEBUG("xact.finalize", "added gain_loss, balance = " << balance); + DEBUG("xact.finalize", "gain_loss = " << gain_loss); + gain_loss.in_place_round(); + DEBUG("xact.finalize", "gain_loss rounds to = " << gain_loss); + + if (post->must_balance()) + add_or_set_value(balance, gain_loss.reduced()); + + account_t * account; + if (gain_loss.sign() > 0) + account = journal->find_account(_("Equity:Capital Gains")); + else + account = journal->find_account(_("Equity:Capital Losses")); + + post_t * p = new post_t(account, gain_loss, ITEM_GENERATED); + p->set_state(post->state()); + if (post->has_flags(POST_VIRTUAL)) { + DEBUG("xact.finalize", "gain_loss came from a virtual post"); + p->add_flags(post->flags() & (POST_VIRTUAL | POST_MUST_BALANCE)); + } + add_post(p); + DEBUG("xact.finalize", "added gain_loss, balance = " << balance); } else { - DEBUG("xact.finalize", "gain_loss would have display as zero"); + DEBUG("xact.finalize", "gain_loss would have display as zero"); } } else { post->amount = breakdown.amount; @@ -315,7 +315,7 @@ bool xact_base_t::finalize() } if (post->has_flags(POST_COST_FIXATED) && - post->amount.has_annotation() && post->amount.annotation().price) { + post->amount.has_annotation() && post->amount.annotation().price) { DEBUG("xact.finalize", "fixating annotation price"); post->amount.annotation().add_flags(ANNOTATION_PRICE_FIXATED); } @@ -332,16 +332,16 @@ bool xact_base_t::finalize() bool first = true; const balance_t& bal(balance.as_balance()); foreach (const balance_t::amounts_map::value_type& pair, bal.amounts) { - if (first) { - null_post->amount = pair.second.negated(); - null_post->add_flags(POST_CALCULATED); - first = false; - } else { - post_t * p = new post_t(null_post->account, pair.second.negated(), - ITEM_GENERATED | POST_CALCULATED); - p->set_state(null_post->state()); - add_post(p); - } + if (first) { + null_post->amount = pair.second.negated(); + null_post->add_flags(POST_CALCULATED); + first = false; + } else { + post_t * p = new post_t(null_post->account, pair.second.negated(), + ITEM_GENERATED | POST_CALCULATED); + p->set_state(null_post->state()); + add_post(p); + } } } else if (balance.is_amount()) { @@ -377,10 +377,10 @@ bool xact_base_t::finalize() foreach (post_t * post, posts) { if (! post->amount.is_null()) { - all_null = false; - post->amount.in_place_reduce(); + all_null = false; + post->amount.in_place_reduce(); } else { - some_null = true; + some_null = true; } post->account->add_post(post); @@ -390,10 +390,10 @@ bool xact_base_t::finalize() } 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")); + _("There cannot be null amounts after balancing a transaction")); } VERIFY(valid()); @@ -419,7 +419,7 @@ bool xact_base_t::verify() // 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().reduced() : p.reduced()); + p.rounded().reduced() : p.reduced()); } VERIFY(balance.valid()); @@ -433,7 +433,7 @@ bool xact_base_t::verify() if (post->amount.commodity() == post->cost->commodity()) throw_(amount_error, - _("A posting's cost must be of a different commodity than its amount")); + _("A posting's cost must be of a different commodity than its amount")); } if (! balance.is_null() && ! balance.is_zero()) { @@ -513,7 +513,7 @@ namespace { foreach (post_t * p, post.xact->posts) { bind_scope_t bound_scope(scope, *p); if (expr.calc(bound_scope).to_boolean()) - return true; + return true; } return false; } @@ -528,14 +528,14 @@ namespace { foreach (post_t * p, post.xact->posts) { bind_scope_t bound_scope(scope, *p); if (! expr.calc(bound_scope).to_boolean()) - return false; + return false; } return true; } } expr_t::ptr_op_t xact_t::lookup(const symbol_t::kind_t kind, - const string& name) + const string& name) { if (kind != symbol_t::FUNCTION) return item_t::lookup(kind, name); @@ -601,13 +601,13 @@ namespace { case expr_t::op_t::O_MATCH: if (op->left()->kind == expr_t::op_t::IDENT && - op->left()->as_ident() == "account" && - op->right()->kind == expr_t::op_t::VALUE && - op->right()->as_value().is_mask()) - return op->right()->as_value().as_mask() - .match(post.reported_account()->fullname()); + op->left()->as_ident() == "account" && + op->right()->kind == expr_t::op_t::VALUE && + op->right()->as_value().is_mask()) + return op->right()->as_value().as_mask() + .match(post.reported_account()->fullname()); else - break; + break; case expr_t::op_t::O_NOT: return ! post_pred(op->left(), post); @@ -620,9 +620,9 @@ namespace { case expr_t::op_t::O_QUERY: if (post_pred(op->left(), post)) - return post_pred(op->right()->left(), post); + return post_pred(op->right()->left(), post); else - return post_pred(op->right()->right(), post); + return post_pred(op->right()->right(), post); default: break; @@ -649,109 +649,109 @@ void auto_xact_t::extend_xact(xact_base_t& xact) bool matches_predicate = false; if (try_quick_match) { try { - bool found_memoized_result = false; - if (! memoized_results.empty()) { - std::map<string, bool>::iterator i = - memoized_results.find(initial_post->account->fullname()); - if (i != memoized_results.end()) { - found_memoized_result = true; - matches_predicate = (*i).second; - } - } - - // Since the majority of people who use automated transactions simply - // match against account names, try using a *much* faster version of - // the predicate evaluator. - if (! found_memoized_result) { - matches_predicate = post_pred(predicate.get_op(), *initial_post); - memoized_results.insert - (std::pair<string, bool>(initial_post->account->fullname(), - matches_predicate)); - } + bool found_memoized_result = false; + if (! memoized_results.empty()) { + std::map<string, bool>::iterator i = + memoized_results.find(initial_post->account->fullname()); + if (i != memoized_results.end()) { + found_memoized_result = true; + matches_predicate = (*i).second; + } + } + + // Since the majority of people who use automated transactions simply + // match against account names, try using a *much* faster version of + // the predicate evaluator. + if (! found_memoized_result) { + matches_predicate = post_pred(predicate.get_op(), *initial_post); + memoized_results.insert + (std::pair<string, bool>(initial_post->account->fullname(), + matches_predicate)); + } } catch (...) { - DEBUG("xact.extend.fail", - "The quick matcher failed, going back to regular eval"); - try_quick_match = false; - matches_predicate = predicate(*initial_post); + DEBUG("xact.extend.fail", + "The quick matcher failed, going back to regular eval"); + try_quick_match = false; + matches_predicate = predicate(*initial_post); } } else { matches_predicate = predicate(*initial_post); } if (matches_predicate) { foreach (post_t * post, posts) { - amount_t post_amount; - if (post->amount.is_null()) { - if (! post->amount_expr) - throw_(amount_error, - _("Automated transaction's posting has no amount")); - - bind_scope_t bound_scope(*scope_t::default_scope, *initial_post); - value_t result(post->amount_expr->calc(bound_scope)); - if (result.is_long()) { - post_amount = result.to_amount(); - } else { - if (! result.is_amount()) - throw_(amount_error, - _("Amount expressions must result in a simple amount")); - post_amount = result.as_amount(); - } - } else { - post_amount = post->amount; - } - - amount_t amt; - if (! post_amount.commodity()) - amt = initial_post->amount * post_amount; - else - amt = post_amount; + amount_t post_amount; + if (post->amount.is_null()) { + if (! post->amount_expr) + throw_(amount_error, + _("Automated transaction's posting has no amount")); + + bind_scope_t bound_scope(*scope_t::default_scope, *initial_post); + value_t result(post->amount_expr->calc(bound_scope)); + if (result.is_long()) { + post_amount = result.to_amount(); + } else { + if (! result.is_amount()) + throw_(amount_error, + _("Amount expressions must result in a simple amount")); + post_amount = result.as_amount(); + } + } else { + post_amount = post->amount; + } + + amount_t amt; + if (! post_amount.commodity()) + amt = initial_post->amount * post_amount; + else + amt = post_amount; #if defined(DEBUG_ON) - IF_DEBUG("xact.extend") { - DEBUG("xact.extend", - "Initial post on line " << initial_post->pos->beg_line << ": " - << "amount " << initial_post->amount << " (precision " - << initial_post->amount.precision() << ")"); - - if (initial_post->amount.keep_precision()) - DEBUG("xact.extend", " precision is kept"); - - DEBUG("xact.extend", - "Posting on line " << post->pos->beg_line << ": " - << "amount " << post_amount << ", amt " << amt - << " (precision " << post_amount.precision() - << " != " << amt.precision() << ")"); - - if (post_amount.keep_precision()) - DEBUG("xact.extend", " precision is kept"); - if (amt.keep_precision()) - DEBUG("xact.extend", " amt precision is kept"); - } + IF_DEBUG("xact.extend") { + DEBUG("xact.extend", + "Initial post on line " << initial_post->pos->beg_line << ": " + << "amount " << initial_post->amount << " (precision " + << initial_post->amount.precision() << ")"); + + if (initial_post->amount.keep_precision()) + DEBUG("xact.extend", " precision is kept"); + + DEBUG("xact.extend", + "Posting on line " << post->pos->beg_line << ": " + << "amount " << post_amount << ", amt " << amt + << " (precision " << post_amount.precision() + << " != " << amt.precision() << ")"); + + if (post_amount.keep_precision()) + DEBUG("xact.extend", " precision is kept"); + if (amt.keep_precision()) + DEBUG("xact.extend", " amt precision is kept"); + } #endif // defined(DEBUG_ON) - account_t * account = post->account; - string fullname = account->fullname(); - assert(! fullname.empty()); - - if (contains(fullname, "$account")) { - fullname = regex_replace(fullname, regex("\\$account\\>"), - initial_post->account->fullname()); - while (account->parent) - account = account->parent; - account = account->find_account(fullname); - } - - // Copy over details so that the resulting post is a mirror of - // the automated xact's one. - post_t * new_post = new post_t(account, amt); - new_post->copy_details(*post); - new_post->add_flags(ITEM_GENERATED); - - xact.add_post(new_post); - new_post->account->add_post(new_post); - - if (new_post->must_balance()) - needs_further_verification = true; + account_t * account = post->account; + string fullname = account->fullname(); + assert(! fullname.empty()); + + if (contains(fullname, "$account")) { + fullname = regex_replace(fullname, regex("\\$account\\>"), + initial_post->account->fullname()); + while (account->parent) + account = account->parent; + account = account->find_account(fullname); + } + + // Copy over details so that the resulting post is a mirror of + // the automated xact's one. + post_t * new_post = new post_t(account, amt); + new_post->copy_details(*post); + new_post->add_flags(ITEM_GENERATED); + + xact.add_post(new_post); + new_post->account->add_post(new_post); + + if (new_post->must_balance()) + needs_further_verification = true; } } } @@ -768,7 +768,7 @@ void auto_xact_t::extend_xact(xact_base_t& xact) } void extend_xact_base(journal_t * journal, - xact_base_t& base) + xact_base_t& base) { foreach (auto_xact_t * xact, journal->auto_xacts) xact->extend_xact(base); @@ -816,18 +816,18 @@ void to_xml(std::ostream& out, const xact_t& xact) push_xml y(out, "metadata"); foreach (const item_t::string_map::value_type& pair, *xact.metadata) { if (pair.second.first) { - push_xml z(out, "variable"); - { - push_xml w(out, "key"); - out << y.guard(pair.first); - } - { - push_xml w(out, "value"); - out << y.guard(*pair.second.first); - } + push_xml z(out, "variable"); + { + push_xml w(out, "key"); + out << y.guard(pair.first); + } + { + push_xml w(out, "value"); + out << y.guard(*pair.second.first); + } } else { - push_xml z(out, "tag"); - out << y.guard(pair.first); + push_xml z(out, "tag"); + out << y.guard(pair.first); } } } |