From 634aa589cd97d088524ae2fb68ec6120d5e4a873 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 13 Apr 2014 23:25:02 -0500 Subject: The option --permissive now quiets balance assertions --- src/textual.cc | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'src/textual.cc') diff --git a/src/textual.cc b/src/textual.cc index 627a1835..fa84d6de 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -77,16 +77,18 @@ namespace { std::istream& in; instance_t * parent; std::list apply_stack; + bool no_assertions; #if defined(TIMELOG_SUPPORT) time_log_t timelog; #endif instance_t(parse_context_stack_t& _context_stack, parse_context_t& _context, - instance_t * _parent = NULL) + instance_t * _parent = NULL, + const bool _no_assertions = false) : context_stack(_context_stack), context(_context), in(*context.stream.get()), parent(_parent), - timelog(context) {} + no_assertions(_no_assertions), timelog(context) {} virtual string description() { return _("textual parser"); @@ -779,8 +781,8 @@ void instance_t::include_directive(char * line) context_stack.get_current().master = master; context_stack.get_current().scope = scope; try { - instance_t instance(context_stack, - context_stack.get_current(), this); + instance_t instance(context_stack, context_stack.get_current(), + this, no_assertions); instance.apply_stack.push_front(application_t("account", master)); instance.parse(); } @@ -1625,7 +1627,7 @@ post_t * instance_t::parse_post(char * line, if (! diff.is_zero()) { if (! post->amount.is_null()) { diff -= post->amount; - if (! diff.is_zero()) + if (! no_assertions && ! diff.is_zero()) throw_(parse_error, _f("Balance assertion off by %1%") % diff); } else { post->amount = diff; @@ -1909,7 +1911,8 @@ std::size_t journal_t::read_textual(parse_context_stack_t& context_stack) { TRACE_START(parsing_total, 1, "Total time spent parsing text:"); { - instance_t instance(context_stack, context_stack.get_current()); + instance_t instance(context_stack, context_stack.get_current(), NULL, + checking_style == journal_t::CHECK_PERMISSIVE); instance.apply_stack.push_front (application_t("account", context_stack.get_current().master)); instance.parse(); -- cgit v1.2.3 From 8f8a94c28e26c0b46a43e89d566c941994645de7 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 13 Apr 2014 23:25:31 -0500 Subject: Add the concept of "deferred postings" This is pretty much exclusively for allowing one to use balance assertions with replicated transactions across multiple files. --- src/account.cc | 15 ++++++++ src/account.h | 17 +++++---- src/journal.cc | 17 ++++++++- src/post.h | 1 + src/textual.cc | 6 +++ src/xact.cc | 5 ++- test/baseline/feat-balance_assert_split.test | 55 ++++++++++++++++++++++++++++ 7 files changed, 107 insertions(+), 9 deletions(-) create mode 100644 test/baseline/feat-balance_assert_split.test (limited to 'src/textual.cc') diff --git a/src/account.cc b/src/account.cc index 4b3a4e97..a702cf11 100644 --- a/src/account.cc +++ b/src/account.cc @@ -139,6 +139,21 @@ void account_t::add_post(post_t * post) } } +void account_t::add_deferred_post(const string& uuid, post_t * post) +{ + if (! deferred_posts) + deferred_posts = deferred_posts_map_t(); + + deferred_posts_map_t::iterator i = deferred_posts->find(uuid); + if (i == deferred_posts->end()) { + posts_list lst; + lst.push_back(post); + deferred_posts->insert(deferred_posts_map_t::value_type(uuid, lst)); + } else { + (*i).second.push_back(post); + } +} + bool account_t::remove_post(post_t * post) { // It's possible that 'post' wasn't yet in this account, but try to diff --git a/src/account.h b/src/account.h index f6f764cc..3ce93fba 100644 --- a/src/account.h +++ b/src/account.h @@ -52,6 +52,7 @@ class post_t; typedef std::list posts_list; typedef std::map accounts_map; +typedef std::map deferred_posts_map_t; class account_t : public supports_flags<>, public scope_t { @@ -61,13 +62,14 @@ class account_t : public supports_flags<>, public scope_t #define ACCOUNT_GENERATED 0x04 // account never actually existed public: - account_t * parent; - string name; - optional note; - unsigned short depth; - accounts_map accounts; - posts_list posts; - optional value_expr; + account_t * parent; + string name; + optional note; + unsigned short depth; + accounts_map accounts; + posts_list posts; + optional deferred_posts; + optional value_expr; mutable string _fullname; #if DOCUMENT_MODEL @@ -136,6 +138,7 @@ public: } void add_post(post_t * post); + void add_deferred_post(const string& uuid, post_t * post); bool remove_post(post_t * post); posts_list::iterator posts_begin() { diff --git a/src/journal.cc b/src/journal.cc index ef35722c..94a535d7 100644 --- a/src/journal.cc +++ b/src/journal.cc @@ -385,9 +385,24 @@ bool journal_t::add_xact(xact_t * xact) // will have been performed by extend_xact, so asserts can still be // applied to it. if (optional ref = xact->get_tag(_("UUID"))) { + std::string uuid = ref->to_string(); std::pair result - = checksum_map.insert(checksum_map_t::value_type(ref->to_string(), xact)); + = checksum_map.insert(checksum_map_t::value_type(uuid, xact)); if (! result.second) { + // This UUID has been seen before; apply any postings which the + // earlier version may have deferred. + foreach (post_t * post, xact->posts) { + account_t * acct = post->account; + if (acct->deferred_posts) { + deferred_posts_map_t::iterator + i = acct->deferred_posts->find(uuid); + if (i != acct->deferred_posts->end()) { + foreach (post_t * rpost, (*i).second) + acct->add_post(rpost); + } + } + } + // jww (2012-02-27): Confirm that the xact in // (*result.first).second is exact match in its significant // details to xact. diff --git a/src/post.h b/src/post.h index 379844dc..51a75ade 100644 --- a/src/post.h +++ b/src/post.h @@ -60,6 +60,7 @@ public: #define POST_COST_FIXATED 0x0200 // cost is fixed using = indicator #define POST_COST_VIRTUAL 0x0400 // cost is virtualized: (@) #define POST_ANONYMIZED 0x0800 // a temporary, anonymous posting +#define POST_DEFERRED 0x1000 // the account was specified with xact_t * xact; // only set for posts of regular xacts account_t * account; diff --git a/src/textual.cc b/src/textual.cc index fa84d6de..6f8f0492 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -1432,6 +1432,12 @@ post_t * instance_t::parse_post(char * line, } p++; e--; } + else if (*p == '<' && *(e - 1) == '>') { + post->add_flags(POST_DEFERRED); + DEBUG("textual.parse", "line " << context.linenum << ": " + << "Parsed a deferred account name"); + p++; e--; + } string name(p, static_cast(e - p)); DEBUG("textual.parse", "line " << context.linenum << ": " diff --git a/src/xact.cc b/src/xact.cc index 58d2ac33..c7a9c6e3 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -393,7 +393,10 @@ bool xact_base_t::finalize() some_null = true; } - post->account->add_post(post); + if (post->has_flags(POST_DEFERRED)) + post->account->add_deferred_post(id(), post); + else + post->account->add_post(post); post->xdata().add_flags(POST_EXT_VISITED); post->account->xdata().add_flags(ACCOUNT_EXT_VISITED); diff --git a/test/baseline/feat-balance_assert_split.test b/test/baseline/feat-balance_assert_split.test new file mode 100644 index 00000000..7c2450b6 --- /dev/null +++ b/test/baseline/feat-balance_assert_split.test @@ -0,0 +1,55 @@ +;; a.dat + +2012-01-01 Test + Expenses:Unknown $100.00 + Liabilities:MasterCard + +2012-01-02 Test + Expenses:Unknown $100.00 + Liabilities:MasterCard + +2012-01-03 Test + Expenses:Unknown $100.00 + Liabilities:MasterCard + +2012-01-04 Test + ; UUID: foo + Liabilities:MasterCard $150.00 = $-300 + + +2012-01-04 Test + ; UUID: bar + Liabilities:MasterCard $150.00 = $0 + + +;; b.dat + +2012-01-01 Test + Assets:Checking $150.00 + Income + +2012-01-02 Test + Assets:Checking $150.00 + Income + +2012-01-03 Test + Assets:Checking $150.00 + Income + +2012-01-04 Test + ; UUID: foo + Liabilities:MasterCard $150.00 + Assets:Checking $-150.00 = $300.00 + +2012-01-04 Test + ; UUID: bar + Liabilities:MasterCard $150.00 + Assets:Checking $-150.00 = $150.00 + +test balance + $150.00 Assets:Checking + $300.00 Expenses:Unknown + $-450.00 Income +-------------------- + 0 +end test -- cgit v1.2.3 From 0d23e3d4f6ea4e9ec9a70211abb892383b80188e Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 14 Apr 2014 11:08:26 -0500 Subject: Apply any outstanding deferred postings at the end of parsing --- src/account.cc | 15 +++++++++++++++ src/account.h | 1 + src/journal.cc | 1 + src/textual.cc | 3 +++ test/baseline/feat-balance_assert_split.test | 5 +++++ 5 files changed, 25 insertions(+) (limited to 'src/textual.cc') diff --git a/src/account.cc b/src/account.cc index a702cf11..216b15bd 100644 --- a/src/account.cc +++ b/src/account.cc @@ -154,6 +154,21 @@ void account_t::add_deferred_post(const string& uuid, post_t * post) } } +void account_t::apply_deferred_posts() +{ + if (deferred_posts) { + foreach (deferred_posts_map_t::value_type& pair, *deferred_posts) { + foreach (post_t * post, pair.second) + post->account->add_post(post); + } + deferred_posts = none; + } + + // Also apply in child accounts + foreach (const accounts_map::value_type& pair, accounts) + pair.second->apply_deferred_posts(); +} + bool account_t::remove_post(post_t * post) { // It's possible that 'post' wasn't yet in this account, but try to diff --git a/src/account.h b/src/account.h index 3ce93fba..d1377c39 100644 --- a/src/account.h +++ b/src/account.h @@ -139,6 +139,7 @@ public: void add_post(post_t * post); void add_deferred_post(const string& uuid, post_t * post); + void apply_deferred_posts(); bool remove_post(post_t * post); posts_list::iterator posts_begin() { diff --git a/src/journal.cc b/src/journal.cc index 94a535d7..ea74cb66 100644 --- a/src/journal.cc +++ b/src/journal.cc @@ -399,6 +399,7 @@ bool journal_t::add_xact(xact_t * xact) if (i != acct->deferred_posts->end()) { foreach (post_t * rpost, (*i).second) acct->add_post(rpost); + acct->deferred_posts->erase(i); } } } diff --git a/src/textual.cc b/src/textual.cc index 6f8f0492..d02e2f79 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -1925,6 +1925,9 @@ std::size_t journal_t::read_textual(parse_context_stack_t& context_stack) } TRACE_STOP(parsing_total, 1); + // Apply any deferred postings at this time + master->apply_deferred_posts(); + // These tracers were started in textual.cc TRACE_FINISH(xact_text, 1); TRACE_FINISH(xact_details, 1); diff --git a/test/baseline/feat-balance_assert_split.test b/test/baseline/feat-balance_assert_split.test index 7c2450b6..1640bf8b 100644 --- a/test/baseline/feat-balance_assert_split.test +++ b/test/baseline/feat-balance_assert_split.test @@ -22,6 +22,11 @@ Liabilities:MasterCard $150.00 = $0 +2012-01-04 Test + ; UUID: baz + Liabilities:MasterCard $150.00 = $0 + + ;; b.dat 2012-01-01 Test -- cgit v1.2.3 From aa2ff2b5db4afd28fc05e4cb0848d6cc1f0862e9 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 16 Apr 2014 15:56:30 -0500 Subject: Improve an error message relating to balance assertions --- src/textual.cc | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'src/textual.cc') diff --git a/src/textual.cc b/src/textual.cc index d02e2f79..55f22a2c 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -1609,22 +1609,25 @@ post_t * instance_t::parse_post(char * line, "line " << context.linenum << ": " << "post amount = " << amt); amount_t diff = amt; + amount_t tot; switch (account_total.type()) { case value_t::AMOUNT: - diff -= account_total.as_amount(); + tot = account_total.as_amount(); break; case value_t::BALANCE: if (optional comm_bal = account_total.as_balance().commodity_amount(amt.commodity())) - diff -= *comm_bal; + tot = *comm_bal; break; default: break; } + diff -= tot; + DEBUG("post.assign", "line " << context.linenum << ": " << "diff = " << diff); DEBUG("textual.parse", "line " << context.linenum << ": " @@ -1634,7 +1637,9 @@ post_t * instance_t::parse_post(char * line, if (! post->amount.is_null()) { diff -= post->amount; if (! no_assertions && ! diff.is_zero()) - throw_(parse_error, _f("Balance assertion off by %1%") % diff); + throw_(parse_error, + _f("Balance assertion off by %1% (expected to see %2%)") + % diff % tot); } else { post->amount = diff; DEBUG("textual.parse", "line " << context.linenum << ": " -- cgit v1.2.3