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/journal.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/journal.cc') diff --git a/src/journal.cc b/src/journal.cc index 160abe06..ef35722c 100644 --- a/src/journal.cc +++ b/src/journal.cc @@ -95,7 +95,7 @@ void journal_t::initialize() force_checking = false; check_payees = false; day_break = false; - checking_style = CHECK_PERMISSIVE; + checking_style = CHECK_NORMAL; recursive_aliases = false; } -- 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/journal.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/journal.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 f73ff40a8671f323c277197417f18af989087950 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 15 Apr 2014 01:31:47 -0500 Subject: Add a safety check when using deferred postings --- src/journal.cc | 50 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 4 deletions(-) (limited to 'src/journal.cc') diff --git a/src/journal.cc b/src/journal.cc index ea74cb66..2512664f 100644 --- a/src/journal.cc +++ b/src/journal.cc @@ -363,6 +363,21 @@ namespace { } } +bool lt_posting_account(post_t * left, post_t * right) { + return left->account < right->account; +} + +bool is_equivalent_posting(post_t * left, post_t * right) +{ + if (left->account != right->account) + return false; + + if (left->amount != right->amount) + return false; + + return true; +} + bool journal_t::add_xact(xact_t * xact) { xact->journal = this; @@ -398,15 +413,42 @@ bool journal_t::add_xact(xact_t * xact) i = acct->deferred_posts->find(uuid); if (i != acct->deferred_posts->end()) { foreach (post_t * rpost, (*i).second) - acct->add_post(rpost); + if (acct == rpost->account) + acct->add_post(rpost); acct->deferred_posts->erase(i); } } } - // jww (2012-02-27): Confirm that the xact in - // (*result.first).second is exact match in its significant - // details to xact. + xact_t * other = (*result.first).second; + + // Copy the two lists of postings (which should be relatively + // short), and make sure that the intersection is the empty set + // (i.e., that they are the same list). + std::vector this_posts(xact->posts.begin(), + xact->posts.end()); + std::sort(this_posts.begin(), this_posts.end(), + lt_posting_account); + std::vector other_posts(other->posts.begin(), + other->posts.end()); + std::sort(other_posts.begin(), other_posts.end(), + lt_posting_account); + bool match = std::equal(this_posts.begin(), this_posts.end(), + other_posts.begin(), is_equivalent_posting); + + if (! match || this_posts.size() != other_posts.size()) { + add_error_context(_("While comparing this previously seen transaction:")); + add_error_context(source_context(other->pos->pathname, + other->pos->beg_pos, + other->pos->end_pos, "> ")); + add_error_context(_("to this later transaction:")); + add_error_context(source_context(xact->pos->pathname, + xact->pos->beg_pos, + xact->pos->end_pos, "> ")); + throw_(std::runtime_error, + _f("Transactions with the same UUID must have equivalent postings")); + } + xact->journal = NULL; return false; } -- cgit v1.2.3 From 7be70aab59051aa358547a3e530cc95490c04232 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 17 Apr 2014 14:19:55 -0500 Subject: Require the use of C++11 --- CMakeLists.txt | 33 ++------------------------------- default.nix | 23 ++++++++++++++++------- src/journal.cc | 5 ++--- src/system.hh.in | 2 -- src/utils.h | 4 ---- 5 files changed, 20 insertions(+), 47 deletions(-) (limited to 'src/journal.cc') diff --git a/CMakeLists.txt b/CMakeLists.txt index 9d709338..e4eb091b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,8 @@ set(Ledger_VERSION_DATE 20140417) enable_testing() +add_definitions(-std=c++11) + ######################################################################## option(USE_PYTHON "Build support for the Python scripting bridge" OFF) @@ -148,37 +150,6 @@ endif() cmake_pop_check_state() -#cmake_push_check_state() -# -#set(CMAKE_REQUIRED_FLAGS -std=c++11) -#set(CMAKE_REQUIRED_INCLUDES ${CMAKE_INCLUDE_PATH}) -# -#check_cxx_source_runs(" -##include -##include -##include -# -#int main() { -# std::vector x {0, 1, 2, 3, 4}; -# for (auto i : x) -# std::cout << i << std::endl; -# -# std::regex r(\"foo\"); -# std::cout << std::regex_match(\"foobar\", r) << std::endl; -# return 0; -#}" CXX11_RUNS) -# -#cmake_pop_check_state() -# -#if(CXX11_RUNS) -# set(HAVE_CXX11 1) -# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") -#else() - set(HAVE_CXX11 0) -#endif() -# -#cmake_pop_check_state() - ######################################################################## include_directories(${CMAKE_INCLUDE_PATH}) diff --git a/default.nix b/default.nix index 7a822bbe..f1745ce4 100644 --- a/default.nix +++ b/default.nix @@ -1,17 +1,26 @@ -{ stdenv, fetchgit, cmake, ninja, boost, gmp, mpfr, libedit, python, texinfo }: +{ stdenv, fetchgit, cmake, boost, gmp, mpfr, libedit, python +, texinfo, gnused }: + +let + rev = "20140417"; +in stdenv.mkDerivation { - name = "ledger-3.0.2"; - version = "3.0.2"; + name = "ledger-3.0.2.${rev}"; src = ./.; - buildInputs = [ cmake ninja boost gmp mpfr libedit python texinfo ]; + buildInputs = [ cmake boost gmp mpfr libedit python texinfo gnused ]; - # Tests on Darwin are failing - doCheck = !stdenv.isDarwin; enableParallelBuilding = true; + # Skip byte-compiling of emacs-lisp files because this is currently + # broken in ledger... + postInstall = '' + mkdir -p $out/share/emacs/site-lisp/ + cp -v $src/lisp/*.el $out/share/emacs/site-lisp/ + ''; + meta = { homepage = "http://ledger-cli.org/"; description = "A double-entry accounting system with a command-line reporting interface"; @@ -25,6 +34,6 @@ stdenv.mkDerivation { ''; platforms = stdenv.lib.platforms.all; - maintainers = with stdenv.lib.maintainers; [ simons the-kenny ]; + maintainers = with stdenv.lib.maintainers; [ simons the-kenny jwiegley ]; }; } diff --git a/src/journal.cc b/src/journal.cc index 2512664f..804c9ee2 100644 --- a/src/journal.cc +++ b/src/journal.cc @@ -409,10 +409,9 @@ bool journal_t::add_xact(xact_t * xact) 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); + auto i = acct->deferred_posts->find(uuid); if (i != acct->deferred_posts->end()) { - foreach (post_t * rpost, (*i).second) + for (post_t * rpost : (*i).second) if (acct == rpost->account) acct->add_post(rpost); acct->deferred_posts->erase(i); diff --git a/src/system.hh.in b/src/system.hh.in index 952411f9..8f501587 100644 --- a/src/system.hh.in +++ b/src/system.hh.in @@ -56,8 +56,6 @@ #define Ledger_VERSION_PATCH @Ledger_VERSION_PATCH@ #define Ledger_VERSION_DATE @Ledger_VERSION_DATE@ -#define HAVE_CXX11 @HAVE_CXX11@ - #define HAVE_EDIT @HAVE_EDIT@ #define HAVE_GETTEXT @HAVE_GETTEXT@ diff --git a/src/utils.h b/src/utils.h index 95cc64fd..c3dcf562 100644 --- a/src/utils.h +++ b/src/utils.h @@ -485,11 +485,7 @@ inline void check_for_signal() { /*@{*/ #define foreach BOOST_FOREACH -#if HAVE_CXX11 using std::unique_ptr; -#else -#define unique_ptr std::auto_ptr -#endif namespace ledger { -- cgit v1.2.3