Skip to content

Commit

Permalink
Add support for fixated prices and costs
Browse files Browse the repository at this point in the history
beancount doesn't have a notion of a fixated price or cost.  However,
you can achieve the same result in beancount.  ledger2beancount will
always convert ledger fixated prices and costs to costs in beancount.
This way, the original cost is always attached to the transaction.
You can then use `SUM(COST(position))` to get the original value.
  • Loading branch information
tbm committed Mar 25, 2019
1 parent e88a39f commit 9b8099e
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 5 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ Please refer to [the manual](docs/manual.md#features) for more details.
* Directives
* `payee`: skipped (not needed in beancount)
* `tag`: skipped (not needed in beancount)
* Fixated prices (`=$10` and the `fixed` directive)
* Tags and links on posting-level (not supported by beancount)
* Transaction codes: stored as metadata (no equivalence in beancount)
* Virtual postings: can be skipped or converted to real postings
Expand All @@ -109,7 +110,6 @@ Please refer to [the manual](docs/manual.md#features) for more details.
* `expr`
* `N`
* Timeclock (`I`, `i`, `O`, `o`, `b`, `h`)
* Fixated prices (`=$10` and the `fixed` directive)
* Periodic transactions


Expand Down
18 changes: 15 additions & 3 deletions bin/ledger2beancount
Original file line number Diff line number Diff line change
Expand Up @@ -768,7 +768,7 @@ sub process_txn(@) {
$l = replace_account $l;
if ($l =~ /^$posting_RE(\s*(?<curlyopen>\{\{?)\s*(?<lot_cost>$amount_RE)\s*(?<curlyclose>\}\}?))?\s*(\[(?<date>$date_RE)\])?\s*(\((?<lot_note>[^@].*)\))?\s*(\(?(?<at>@@?)\)?\s*(?<lot_price>$amount_RE))?(\s*=\s*(?<assertion>$amount_RE))?(\s*$comment_RE)?/) {
if ($l =~ /^$posting_RE(\s*(?<curlyopen>\{\{?)\s*(?<fixated_cost>=\s*)?(?<lot_cost>$amount_RE)\s*(?<curlyclose>\}\}?))?\s*(\[(?<date>$date_RE)\])?\s*(\((?<lot_note>[^@].*)\))?\s*(\(?(?<at>@@?)\)?\s*?(?<fixated_price>=\s*)?(?<lot_price>$amount_RE))?(\s*=\s*(?<assertion>$amount_RE))?(\s*$comment_RE)?/) {
# posting with unit price and optional lot price
# XXX refactor/merge with previous regex case
my $lot_info = "";
Expand All @@ -792,10 +792,10 @@ sub process_txn(@) {
# commodities that should be treated as currencies.
my $commodity1 = map_commodity $+{commodity};
my $commodity2 = map_commodity parse_commodity $+{lot_price};
if ((length $commodity1 == 3 && length $commodity2 == 3 &&
if (!$+{fixated_price} && ((length $commodity1 == 3 && length $commodity2 == 3 &&
!($commodity1 ~~ @{$config->{currency_is_commodity}})) ||
$commodity1 ~~ @{$config->{commodity_is_currency}} ||
$commodity2 ~~ @{$config->{commodity_is_currency}}) {
$commodity2 ~~ @{$config->{commodity_is_currency}})) {
$l = sprintf "$+{posting} %s %s", "@" x length $+{at}, replace_commodity $+{lot_price};
} else {
$l = sprintf "$+{posting} %s%s%s%s", "{" x length $+{at},
Expand All @@ -811,6 +811,16 @@ sub process_txn(@) {
} else {
$l = $+{posting};
}
if (!defined $+{lot_cost}) {
# Apply any fixated costs if needed
foreach my $a (reverse @ledger_apply) {
if (${$a}[0] eq "fixed") {
my ($commodity, $fixated) = split /\s+/, ${$a}[1], 2;
$fixated = replace_commodity $fixated;
$l =~ s/($+{amount})/$1 {$fixated}/ if $+{commodity} eq $commodity;
}
}
}
if ($+{comment}) {
my $comment = handle_comment $depth, "; $+{comment}", 1;
$l .= " ; $comment" if $comment;
Expand Down Expand Up @@ -938,6 +948,8 @@ while (@input) {
$ledger_alias{$+{account}} = map_account_apply $+{val};
} elsif ($l =~ /^[!@]?apply\s+(?<type>account)\s+(?<val>.*)/) { # apply account
push @ledger_apply, [$+{type}, $+{val}];
} elsif ($l =~ /^[!@]?apply\s+(?<type>fixed)\s+(?<val>.*)/) { # apply fixed
push @ledger_apply, [$+{type}, $+{val}];
} elsif ($l =~ /^[!@]?apply\s+(?<type>tag)\s+(?<val>.*)/) { # apply tag
# `apply tag` can be converted to beancount in three ways:
# * using pushtag/poptag for tags
Expand Down
1 change: 1 addition & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## 1.6 (unreleased)

* Add support for fixated prices and costs
* Handle account names that contain brackets
* Don't parse trailing tabs as part of the account name
* Escape backslashes in the narration
Expand Down
15 changes: 14 additions & 1 deletion docs/manual.md
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,20 @@ postings in a transaction (the most common case). More complex
implicit conversations are not supported.


### Fixated prices and costs

ledger allows you to ["fix" the cost or price](https://www.ledger-cli.org/3.0/doc/ledger3.html#Fixated-prices-and-costs)
at the time of a transaction, which means the amount will not be revalued
subsequently when the price of the commodity changes in the `pricedb`.
beancount doesn't have a notion of a fixated price or cost.

However, you can achieve the same result in beancount. ledger2beancount
will always convert ledger fixated prices and costs to costs in
beancount. This way, the original cost is always attached to the
transaction. You can then use `SUM(COST(position))` to get the original
value.


### hledger syntax

The syntax of [hledger](http://hledger.org/) is largely compatible with
Expand Down Expand Up @@ -746,7 +760,6 @@ Unsupported features in ledger2beancount
The following ledger features are currently not supported by
ledger2beancount:

* Fixated prices (`=$10` syntax and the `fixed` directive)
* The `define` directive

Contributions [are welcome!](CONTRIBUTING.md)
Expand Down

0 comments on commit 9b8099e

Please sign in to comment.