A long asked-for feature, pre-declarations, has now arrived!

This feature comes with some breaking changes, even damaging backwards
compatibility with 2.x.  I believe it is worth it for the gain in consistency.

------------------------------------------------------------------------------
# BREAKING CHANGES

## 'account' directive

In 2.x, you could enclose a group of transactions within a parent account:

  account My Master
  ...
  end account

This is now done with "apply account" instead of "account":

  apply account My Master
  ...
  end apply

You can also use "end apply account", and Ledger will verify that it matches
an enclosing "apply account".

## 'tag' directive

In 3.x, you could apply a tag to a group of transactions:

  tag Foo: Bar
  ...
  end tag

This is now done with "apply tag" instead of "tag":

  apply tag Foo: Bar
  ...
  end apply tag

------------------------------------------------------------------------------
# NEW DIRECTIVES

There are four all new directives:

  account
  payee
  commodity
  tag

## New 'account' directive

You can now pre-declare account names.  This only has effect if --strict or
--pedantic is used (see below).

  account Expenses:Food
  account Expenses:Gas

### Account sub-directives

The 'account' directive supports several optional sub-directives, if they
immediately follow the account directive and if they begin with whitespace:

  account Expenses:Food
      note This account is all about the chicken!
      alias food
      payee ^(KFC|Popeyes)$
      check commodity == "$"
      assert commodity == "$"
      eval print("Hello!")
      default

The 'note' sub-directive associates a textual note with the account.  This can
be accessed later using the 'note' valexpr function in any account context.

The 'alias' sub-directive, which can occur multiple times, allows the alias to
be used in place of the full account name anywhere that account names are
allowed.

The 'payee' sub-directive, which can occur multiple times, provides regexps
that identify the account if that payee is encountered and an account within
its transaction ends in the name "Unknown".  Example:

  2012-02-27 KFC
      Expenses:Unknown      $10.00  ; Read now as "Expenses:Food"
      Assets:Cash

The 'check' and 'assert' directives warn or error (respectively) if the given
value expression evaluates to false within the context of any posting.

The 'eval' directive evaluates the value expression in the context of the
account at the time of definition.  At the moment this has little value.

The 'default' directive specifies that this account should be used as the
"balancing account" for any future transactions that contain only a single
posting.

## New 'payee' directive

You can now pre-declare payee names.  This only has effect if --check-payees
is used in addition to --strict or --pedantic (see below).

  payee KFC
  payee Payless

### Payee sub-directives

The 'payee' directive supports one optional sub-directive, if it immediately
follows the payee directive and if it begins with whitespace:

  payee KFC
      alias KENTUCKY FRIED CHICKEN

The 'alias' directive provides a regexp which, if it matches a parsed payee,
the declared payee name is substituted:

  2012-02-27 KENTUCKY FRIED CHICKEN  ; will be read as being 'KFC'
    ...

## New 'commodity' directive

You can now pre-declare commodity names.  This only has effect if --strict or
--pedantic is used (see below).

  commodity $
  commodity CAD

### Commodity sub-directives

The 'commodity' directive supports several optional sub-directives, if they
immediately follow the commodity directive and if they begin with whitespace:

  commodity $
    note American Dollars
    format $1,000.00
    nomarket
    default

The 'note' sub-directive associates a textual note with the commodity.  At
present this has no value other than documentation.

The 'format' directive gives you a way to tell Ledger how to format this
commodity.  In future using this directive will disable Ledger's observation
of other ways that commodity is used, and will provide the "canonical"
representation.

The 'nomarket' directive states that the commodity's price should never be
auto-downloaded.

The 'default' directive marks this as the "default" commodity, in contexts
where that applies (the same as the current 'D' directive).

## New 'tag' directive

You can now pre-declare tag names.  This only has effect if --strict or
--pedantic is used (see below).

  tag Receipt
  tag CSV
  
### Tag sub-directives

The 'tag' directive supports two optional sub-directives, if they immediately
follow the tag directive and if they begin with whitespace:

  tag Receipt
    check value =~ /pattern/
    assert value != "foobar"

The 'check' and 'assert' directives warn or error (respectively) if the given
value expression evaluates to false within the context of any use of the
related tag.  In such a context, "value" is bound to the value of the tag
(which may not be a string if typed-metadata is used!).  Such checks or
assertions are not called if no value is given.

------------------------------------------------------------------------------
# "KNOWN" ENTITIES

Normally, an account/payee/commodity/tag is considered "known" to Ledger if
it:

  1. Occurs within a predeclaration, using the directives above.
  2. Occurs within a cleared or pending posting or transaction.

Otherwise, if the account/payee/commodity/tag is first encountered in an
uncleared posting or transaction, it is considered "unknown".

By default, this distinction has no meaning whatsoever, and is not maintained
for the sake of speed.  It only has meaning if --strict or --pedantic is used
(see next section).

------------------------------------------------------------------------------
# NEW OPTIONS

## --explicit

When --explicit is given, *only* predeclarations establish the "known-ness" of
parsed entities.  I.e., if you didn't predeclare it, you don't expect to ever
see it.

## --strict

With --strict, referring to unknown entities causes a warning.

## --pedantic

With --pedantic, referring to unknown entities causes an error.

## --check-payees

By default, even with --strict or --pedantic, payees are not checked for
known-ness because it is quite typical that new payees are used in uncleared
transactions, or without declaring them.  The --check-payees option enables
--strict and --pedantic checking for payees as well.

------------------------------------------------------------------------------
# EXAMPLE

Here's a real example from the baseline tests.  Remember that options can be
specified directly within your Ledger file!

  --explicit
  --pedantic
  
  commodity $
      format $1,000.00

  account Assets:Cash
      assert abs(amount) <= 20
      check commodity == '$'
  
  account Expenses:Food
      alias food
      payee KFC
  
  2012-02-27 KFC
      Expenses:Unknown                          $20.00
      Assets:Cash
  
  2012-02-28 KFC
      food                                      $20.00
      Assets:Cash

  test reg --strict
  12-Feb-27 KFC                   Expenses:Food                $20.00       
$20.00
                                  Assets:Cash                 $-20.00           
 0
  12-Feb-28 KFC                   Expenses:Food                $20.00       
$20.00
                                  Assets:Cash                 $-20.00           
 0
  end test

Reply via email to