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.
This commit is contained in:
John Wiegley 2010-06-18 02:26:50 -04:00
parent 7e2547b1e4
commit 5da1e7756d
7 changed files with 89 additions and 1 deletions

View file

@ -1,4 +1,4 @@
.Dd June 15, 2010
.Dd June 18, 2010
.Dt ledger 1
.Sh NAME
.Nm ledger
@ -336,6 +336,7 @@ See
.It Fl \-help-disp
.It Fl \-import Ar STR
.It Fl \-init-file Ar FILE
.It Fl \-inject Ar STR
.It Fl \-input-date-format Ar DATEFMT
.It Fl \-invert
.It Fl \-last Ar INT

View file

@ -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;
}

View file

@ -1338,6 +1338,66 @@ void forecast_posts::flush()
item_handler<post_t>::flush();
}
inject_posts::inject_posts(post_handler_ptr handler,
const string& tag_list,
account_t * master)
: item_handler<post_t>(handler)
{
TRACE_CTOR(inject_posts, "post_handler_ptr, string");
scoped_array<char> 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<string> 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<value_t> 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<post_t>::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<post_t>::operator()(post);
}
pass_down_accounts::pass_down_accounts(acct_handler_ptr handler,
accounts_iterator& iter,
const optional<predicate_t>& _pred,

View file

@ -929,6 +929,26 @@ class forecast_posts : public generate_posts
}
};
class inject_posts : public item_handler<post_t>
{
typedef std::set<xact_t *> tag_injected_set;
typedef std::pair<account_t *, tag_injected_set> tag_mapping_pair;
typedef std::pair<string, tag_mapping_pair> tags_list_pair;
std::list<tags_list_pair> 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

View file

@ -955,6 +955,7 @@ option_t<report_t> * report_t::lookup_option(const char * p)
break;
case 'i':
OPT(invert);
else OPT(inject_);
break;
case 'j':
OPT_CH(amount_data);

View file

@ -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");

View file