From 5da1e7756d2a4eb04753b8a97bc00063b4d3f687 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 18 Jun 2010 02:26:50 -0400 Subject: Added new option --inject=KEY[,KEY...] If you have a typed metadata key which contains an amount, you can use --inject=KEY to inject a posting with that amount wherever a match occurs. There are two main forms of usage: 2010-06-18 Sample ; Key:: $100 Expenses:Food $100.00 Assets:Checking The command would be: ledger reg --inject=Key In the above, transactional form, a posting under the account "Key" will be injected before the first posting reported for this transaction. It's amount will be $100. This only happens once for the whole transaction. It is also possible to associate the key with a posting: 2010-06-18 Sample Expenses:Food $100.00 ; Key:: $100 Assets:Checking Now the injected posting is generated whenever that particular post is reported. --- src/chain.cc | 4 ++++ src/filters.cc | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/filters.h | 20 ++++++++++++++++++++ src/report.cc | 1 + src/report.h | 2 ++ 5 files changed, 87 insertions(+) (limited to 'src') diff --git a/src/chain.cc b/src/chain.cc index 64550663..8010ba74 100644 --- a/src/chain.cc +++ b/src/chain.cc @@ -258,6 +258,10 @@ post_handler_ptr chain_post_handlers(post_handler_ptr base_handler, if (report.HANDLED(related)) handler.reset(new related_posts(handler, report.HANDLED(related_all))); + if (report.HANDLED(inject_)) + handler.reset(new inject_posts(handler, report.HANDLED(inject_).str(), + report.session.journal->master)); + return handler; } diff --git a/src/filters.cc b/src/filters.cc index 6e832149..33905856 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -1338,6 +1338,66 @@ void forecast_posts::flush() item_handler::flush(); } +inject_posts::inject_posts(post_handler_ptr handler, + const string& tag_list, + account_t * master) + : item_handler(handler) +{ + TRACE_CTOR(inject_posts, "post_handler_ptr, string"); + + scoped_array buf(new char[tag_list.length() + 1]); + std::strcpy(buf.get(), tag_list.c_str()); + + for (char * q = std::strtok(buf.get(), ","); + q; + q = std::strtok(NULL, ",")) { + + std::list account_names; + split_string(q, ':', account_names); + account_t * account = + create_temp_account_from_path(account_names, temps, master); + account->add_flags(ACCOUNT_GENERATED); + + tags_list.push_back + (tags_list_pair(q, tag_mapping_pair(account, tag_injected_set()))); + } +} + +void inject_posts::operator()(post_t& post) +{ + foreach (tags_list_pair& pair, tags_list) { + optional tag_value = post.get_tag(pair.first, false); + if (! tag_value && + pair.second.second.find(post.xact) == pair.second.second.end()) { + // When checking if the transaction has the tag, only inject once + // per transaction. + pair.second.second.insert(post.xact); + tag_value = post.xact->get_tag(pair.first); + } + + if (tag_value) { + if (tag_value->is_amount()) { + xact_t& xact = temps.copy_xact(*post.xact); + xact._date = post.date(); + xact.add_flags(ITEM_GENERATED); + post_t& temp = temps.copy_post(post, xact); + + temp.account = pair.second.first; + temp.amount = tag_value->as_amount(); + temp.add_flags(ITEM_GENERATED); + + item_handler::operator()(temp); + } else { + throw_(std::logic_error, + _("Attempt to inject a posting with non-amount %1 for tag %2") + << *tag_value << pair.first); + } + } + } + + item_handler::operator()(post); +} + pass_down_accounts::pass_down_accounts(acct_handler_ptr handler, accounts_iterator& iter, const optional& _pred, diff --git a/src/filters.h b/src/filters.h index 9102d4f1..180253d2 100644 --- a/src/filters.h +++ b/src/filters.h @@ -929,6 +929,26 @@ class forecast_posts : public generate_posts } }; +class inject_posts : public item_handler +{ + typedef std::set tag_injected_set; + typedef std::pair tag_mapping_pair; + typedef std::pair tags_list_pair; + + std::list tags_list; + temporaries_t temps; + + public: + inject_posts(post_handler_ptr handler, const string& tag_list, + account_t * master); + + virtual ~inject_posts() throw() { + TRACE_DTOR(inject_posts); + } + + virtual void operator()(post_t& post); +}; + ////////////////////////////////////////////////////////////////////// // // Account filters diff --git a/src/report.cc b/src/report.cc index 9626283a..da7c11f5 100644 --- a/src/report.cc +++ b/src/report.cc @@ -955,6 +955,7 @@ option_t * report_t::lookup_option(const char * p) break; case 'i': OPT(invert); + else OPT(inject_); break; case 'j': OPT_CH(amount_data); diff --git a/src/report.h b/src/report.h index a453d351..7d725b8c 100644 --- a/src/report.h +++ b/src/report.h @@ -260,6 +260,7 @@ public: HANDLER(group_by_).report(out); HANDLER(group_title_format_).report(out); HANDLER(head_).report(out); + HANDLER(inject_).report(out); HANDLER(invert).report(out); HANDLER(limit_).report(out); HANDLER(lot_dates).report(out); @@ -616,6 +617,7 @@ public: }); OPTION(report_t, head_); + OPTION(report_t, inject_); OPTION_(report_t, invert, DO() { parent->HANDLER(amount_).set_expr(string("--invert"), "-amount"); -- cgit v1.2.3