From b6ff8f19d5158977dcb09ea6cf94b07b00937f4a Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 11 Nov 2009 21:33:07 -0500 Subject: Transactions now verified after applying auto xacts This way you cannot violate the balancing rules, not even by adding a stray posting via an automated transaction. --- src/xact.cc | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) (limited to 'src/xact.cc') diff --git a/src/xact.cc b/src/xact.cc index 8ac5280a..86b55dd5 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -372,6 +372,55 @@ bool xact_base_t::finalize() return true; } +bool xact_base_t::verify() +{ + // Scan through and compute the total balance for the xact. + + value_t balance; + + foreach (post_t * post, posts) { + if (! post->must_balance()) + continue; + + amount_t& p(post->cost ? *post->cost : post->amount); + assert(! p.is_null()); + + // 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()); + } + VERIFY(balance.valid()); + + // 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 + // amounts. + + foreach (post_t * post, posts) { + if (! post->cost) + continue; + + if (post->amount.commodity() == post->cost->commodity()) + throw_(balance_error, + _("A posting's cost must be of a different commodity than its amount")); + } + + if (! balance.is_null() && ! balance.is_zero()) { + add_error_context(item_context(*this, _("While balancing transaction"))); + add_error_context(_("Unbalanced remainder is:")); + add_error_context(value_context(balance)); + add_error_context(_("Amount to balance against:")); + add_error_context(value_context(magnitude())); + throw_(balance_error, _("Transaction does not balance")); + } + + VERIFY(valid()); + + return true; +} + xact_t::xact_t(const xact_t& e) : xact_base_t(e), code(e.code), payee(e.payee) { @@ -486,6 +535,8 @@ void auto_xact_t::extend_xact(xact_base_t& xact) try { + bool needs_further_verification = false; + foreach (post_t * initial_post, initial_posts) { if (! initial_post->has_flags(ITEM_GENERATED) && predicate(*initial_post)) { @@ -555,10 +606,16 @@ void auto_xact_t::extend_xact(xact_base_t& xact) xact.add_post(new_post); new_post->account->add_post(new_post); + + if (new_post->must_balance()) + needs_further_verification = true; } } } + if (needs_further_verification) + xact.verify(); + } catch (const std::exception& err) { add_error_context(item_context(*this, _("While applying automated transaction"))); -- cgit v1.2.3 From 48dc654eda27c01d0bad88674d21d0e33e5472f6 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 12 Nov 2009 05:09:25 -0500 Subject: Added has_xdata() methods for journal_t and xact_t --- src/account.cc | 13 +++++++++++-- src/account.h | 1 + src/journal.cc | 20 ++++++++++++++++++++ src/journal.h | 1 + src/py_journal.cc | 1 + src/py_xact.cc | 1 + src/xact.cc | 9 +++++++++ src/xact.h | 1 + 8 files changed, 45 insertions(+), 2 deletions(-) (limited to 'src/xact.cc') diff --git a/src/account.cc b/src/account.cc index 5cc7e070..da43745a 100644 --- a/src/account.cc +++ b/src/account.cc @@ -327,16 +327,25 @@ bool account_t::valid() const return true; } +bool account_t::children_with_xdata() const +{ + foreach (const accounts_map::value_type& pair, accounts) + if (pair.second->has_xdata() || + pair.second->children_with_xdata()) + return true; + + return false; +} + std::size_t account_t::children_with_flags(xdata_t::flags_t flags) const { std::size_t count = 0; bool grandchildren_visited = false; - foreach (const accounts_map::value_type& pair, accounts) { + foreach (const accounts_map::value_type& pair, accounts) if (pair.second->has_xflags(flags) || pair.second->children_with_flags(flags)) count++; - } // Although no immediately children were visited, if any progeny at all were // visited, it counts as one. diff --git a/src/account.h b/src/account.h index 0ac1aeb6..1e56fe23 100644 --- a/src/account.h +++ b/src/account.h @@ -241,6 +241,7 @@ public: bool has_xflags(xdata_t::flags_t flags) const { return xdata_ && xdata_->has_flags(flags); } + bool children_with_xdata() const; std::size_t children_with_flags(xdata_t::flags_t flags) const; #if defined(HAVE_BOOST_SERIALIZATION) diff --git a/src/journal.cc b/src/journal.cc index c764dbce..2366ce30 100644 --- a/src/journal.cc +++ b/src/journal.cc @@ -211,6 +211,26 @@ std::size_t journal_t::read(const path& pathname, return count; } +bool journal_t::has_xdata() +{ + foreach (xact_t * xact, xacts) + if (xact->has_xdata()) + return true; + + foreach (auto_xact_t * xact, auto_xacts) + if (xact->has_xdata()) + return true; + + foreach (period_xact_t * xact, period_xacts) + if (xact->has_xdata()) + return true; + + if (master->has_xdata() || master->children_with_xdata()) + return true; + + return false; +} + void journal_t::clear_xdata() { foreach (xact_t * xact, xacts) diff --git a/src/journal.h b/src/journal.h index 68fde517..f7124736 100644 --- a/src/journal.h +++ b/src/journal.h @@ -172,6 +172,7 @@ public: const path * original_file = NULL, bool strict = false); + bool has_xdata(); void clear_xdata(); bool valid() const; diff --git a/src/py_journal.cc b/src/py_journal.cc index 7fc1561d..51979438 100644 --- a/src/py_journal.cc +++ b/src/py_journal.cc @@ -197,6 +197,7 @@ void export_journal() .def("read", py_read) + .def("has_xdata", &journal_t::has_xdata) .def("clear_xdata", &journal_t::clear_xdata) .def("valid", &journal_t::valid) diff --git a/src/py_xact.cc b/src/py_xact.cc index 81847656..59c599d9 100644 --- a/src/py_xact.cc +++ b/src/py_xact.cc @@ -122,6 +122,7 @@ void export_xact() .def("lookup", &xact_t::lookup) + .def("has_xdata", &xact_t::has_xdata) .def("clear_xdata", &xact_t::clear_xdata) .def("valid", &xact_t::valid) diff --git a/src/xact.cc b/src/xact.cc index 86b55dd5..1cece187 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -76,6 +76,15 @@ bool xact_base_t::remove_post(post_t * post) return true; } +bool xact_base_t::has_xdata() +{ + foreach (post_t * post, posts) + if (post->has_xdata()) + return true; + + return false; +} + void xact_base_t::clear_xdata() { foreach (post_t * post, posts) diff --git a/src/xact.h b/src/xact.h index 933a5fd7..fe748fcc 100644 --- a/src/xact.h +++ b/src/xact.h @@ -80,6 +80,7 @@ public: bool finalize(); bool verify(); + bool has_xdata(); void clear_xdata(); virtual bool valid() const { -- cgit v1.2.3