imp: generate auto postings on forecast transactions by default

This commit is contained in:
Simon Michael 2023-04-27 22:21:01 -10:00
parent 765742ab9c
commit 2b5194238b
6 changed files with 75 additions and 52 deletions

View File

@ -785,13 +785,16 @@ journalUntieTransactions t@Transaction{tpostings=ps} = t{tpostings=map (\p -> p{
-- postings to transactions, eg). Or if a modifier rule fails to parse, -- postings to transactions, eg). Or if a modifier rule fails to parse,
-- return the error message. A reference date is provided to help interpret -- return the error message. A reference date is provided to help interpret
-- relative dates in transaction modifier queries. -- relative dates in transaction modifier queries.
journalModifyTransactions :: Day -> Journal -> Either String Journal -- The first argument selects whether to modify only generated (--forecast) transactions (False),
journalModifyTransactions d j = -- or all transactions (True).
case modifyTransactions (journalAccountType j) (journalInheritedAccountTags j) (journalCommodityStyles j) d (jtxnmodifiers j) (jtxns j) of journalModifyTransactions :: Bool -> Day -> Journal -> Either String Journal
journalModifyTransactions alltxns d j =
case modifyTransactions predfn (journalAccountType j) (journalInheritedAccountTags j) (journalCommodityStyles j) d (jtxnmodifiers j) (jtxns j) of
Right ts -> Right j{jtxns=ts} Right ts -> Right j{jtxns=ts}
Left err -> Left err Left err -> Left err
where
-- predfn = if alltxns then const True else isgenerated
isgenerated = matchesTransaction (Tag (toRegex' "_generated-transaction") Nothing)
-- | Choose and apply a consistent display style to the posting -- | Choose and apply a consistent display style to the posting
-- amounts in each commodity (see journalCommodityStyles). -- amounts in each commodity (see journalCommodityStyles).

View File

@ -25,7 +25,8 @@ import Hledger.Data.Transaction (txnTieKnot)
import Hledger.Query (Query, filterQuery, matchesAmount, matchesPostingExtra, import Hledger.Query (Query, filterQuery, matchesAmount, matchesPostingExtra,
parseQuery, queryIsAmt, queryIsSym, simplifyQuery) parseQuery, queryIsAmt, queryIsSym, simplifyQuery)
import Hledger.Data.Posting (commentJoin, commentAddTag, postingAddTags, postingApplyCommodityStyles) import Hledger.Data.Posting (commentJoin, commentAddTag, postingAddTags, postingApplyCommodityStyles)
import Hledger.Utils (dbg6, wrap) import Hledger.Utils (wrap)
import Hledger.Utils.Debug
-- $setup -- $setup
-- >>> :set -XOverloadedStrings -- >>> :set -XOverloadedStrings
@ -33,25 +34,27 @@ import Hledger.Utils (dbg6, wrap)
-- >>> import Hledger.Data.Transaction -- >>> import Hledger.Data.Transaction
-- >>> import Hledger.Data.Journal -- >>> import Hledger.Data.Journal
-- | Apply all the given transaction modifiers, in turn, to each transaction. -- | Apply all the given transaction modifiers, in turn, to each transaction
-- for which the given predicate is true.
-- Or if any of them fails to be parsed, return the first error. A reference -- Or if any of them fails to be parsed, return the first error. A reference
-- date is provided to help interpret relative dates in transaction modifier -- date is provided to help interpret relative dates in transaction modifier
-- queries. -- queries.
modifyTransactions :: (AccountName -> Maybe AccountType) modifyTransactions :: (Transaction -> Bool)
-> (AccountName -> Maybe AccountType)
-> (AccountName -> [Tag]) -> (AccountName -> [Tag])
-> M.Map CommoditySymbol AmountStyle -> M.Map CommoditySymbol AmountStyle
-> Day -> [TransactionModifier] -> [Transaction] -> Day -> [TransactionModifier] -> [Transaction]
-> Either String [Transaction] -> Either String [Transaction]
modifyTransactions atypes atags styles d tmods ts = do modifyTransactions predfn atypes atags styles d tmods ts = do
fs <- mapM (transactionModifierToFunction atypes atags styles d) tmods -- convert modifiers to functions, or return a parse error fs <- mapM (transactionModifierToFunction atypes atags styles d) tmods -- convert modifiers to functions, or return a parse error
let let
modifytxn t = t'' maybemodifytxn t = if predfn t then t'' else t
where where
t' = foldr (flip (.)) id fs t -- apply each function in turn t' = foldr (flip (.)) id fs t -- apply each function in turn
t'' = if t' == t -- and add some tags if it was changed t'' = if t' == t -- and add some tags if it was changed
then t' then t'
else t'{tcomment=tcomment t' `commentAddTag` ("modified",""), ttags=("modified","") : ttags t'} else t'{tcomment=tcomment t' `commentAddTag` ("modified",""), ttags=("modified","") : ttags t'}
Right $ map modifytxn ts Right $ map maybemodifytxn ts
-- | Converts a 'TransactionModifier' to a 'Transaction'-transforming function -- | Converts a 'TransactionModifier' to a 'Transaction'-transforming function
-- which applies the modification(s) specified by the TransactionModifier. -- which applies the modification(s) specified by the TransactionModifier.

View File

@ -324,8 +324,8 @@ journalFinalise iopts@InputOpts{..} f txt pj = do
& journalApplyCommodityStyles -- Infer and apply commodity styles - should be done early & journalApplyCommodityStyles -- Infer and apply commodity styles - should be done early
<&> journalAddForecast (forecastPeriod iopts pj) -- Add forecast transactions if enabled <&> journalAddForecast (forecastPeriod iopts pj) -- Add forecast transactions if enabled
<&> journalPostingsAddAccountTags -- Add account tags to postings, so they can be matched by auto postings. <&> journalPostingsAddAccountTags -- Add account tags to postings, so they can be matched by auto postings.
>>= (if auto_ && not (null $ jtxnmodifiers pj) >>= (if not (null $ jtxnmodifiers pj)
then journalAddAutoPostings _ioDay balancingopts_ -- Add auto postings if enabled, and account tags if needed then journalAddAutoPostings auto_ _ioDay balancingopts_ -- Add auto postings if enabled, and account tags if needed
else pure) else pure)
-- >>= Right . dbg0With (concatMap (T.unpack.showTransaction).jtxns) -- debug -- >>= Right . dbg0With (concatMap (T.unpack.showTransaction).jtxns) -- debug
>>= journalMarkRedundantCosts -- Mark redundant costs, to help journalBalanceTransactions ignore them >>= journalMarkRedundantCosts -- Mark redundant costs, to help journalBalanceTransactions ignore them
@ -346,14 +346,15 @@ journalFinalise iopts@InputOpts{..} f txt pj = do
return j return j
-- | Apply any auto posting rules to generate extra postings on this journal's transactions. -- | Apply any auto posting rules to generate extra postings on this journal's transactions.
journalAddAutoPostings :: Day -> BalancingOpts -> Journal -> Either String Journal -- With a true first argument, applies them to all transactions, otherwise only to generated transactions.
journalAddAutoPostings d bopts = journalAddAutoPostings :: Bool -> Day -> BalancingOpts -> Journal -> Either String Journal
journalAddAutoPostings alltxns d bopts =
-- Balance all transactions without checking balance assertions, -- Balance all transactions without checking balance assertions,
journalBalanceTransactions bopts{ignore_assertions_=True} journalBalanceTransactions bopts{ignore_assertions_=True}
-- then add the auto postings -- then add the auto postings
-- (Note adding auto postings after balancing means #893b fails; -- (Note adding auto postings after balancing means #893b fails;
-- adding them before balancing probably means #893a, #928, #938 fail.) -- adding them before balancing probably means #893a, #928, #938 fail.)
>=> journalModifyTransactions d >=> journalModifyTransactions alltxns d
-- | Generate periodic transactions from all periodic transaction rules in the journal. -- | Generate periodic transactions from all periodic transaction rules in the journal.
-- These transactions are added to the in-memory Journal (but not the on-disk file). -- These transactions are added to the in-memory Journal (but not the on-disk file).

View File

@ -41,7 +41,7 @@ rewrite opts@CliOpts{rawopts_=rawopts,reportspec_=rspec} j@Journal{jtxns=ts} = d
-- rewrite matched transactions -- rewrite matched transactions
let today = _rsDay rspec let today = _rsDay rspec
let modifiers = transactionModifierFromOpts opts : jtxnmodifiers j let modifiers = transactionModifierFromOpts opts : jtxnmodifiers j
let j' = j{jtxns=either error' id $ modifyTransactions (journalAccountType j) (journalInheritedAccountTags j) mempty today modifiers ts} -- PARTIAL: let j' = j{jtxns=either error' id $ modifyTransactions (const True) (journalAccountType j) (journalInheritedAccountTags j) mempty today modifiers ts} -- PARTIAL:
-- run the print command, showing all transactions, or show diffs -- run the print command, showing all transactions, or show diffs
printOrDiff rawopts opts{reportspec_=rspec{_rsQuery=Any}} j j' printOrDiff rawopts opts{reportspec_=rspec{_rsQuery=Any}} j j'

View File

@ -2436,27 +2436,26 @@ So,
- Do write two spaces between your period expression and your transaction description, if any. - Do write two spaces between your period expression and your transaction description, if any.
- Don't accidentally write two spaces in the middle of your period expression. - Don't accidentally write two spaces in the middle of your period expression.
## Other syntax ## Auto postings
hledger journal format supports quite a few other features, The `=` directive declares a rule for generating temporary extra postings
mainly to make interoperating with or converting from Ledger easier. on transactions. Wherever the rule matches an existing posting, it can
Note some of the features below are powerful and can be useful in special cases, add one or more companion postings below that one, optionally influenced
but in general, features in this section are considered less important by the matched posting's amount. This can be useful for generating
or even not recommended for most users. tax postings with a standard percentage, for example.
Downsides are mentioned to help you decide if you want to use them.
### Auto postings By default, these auto posting rules are applied to transactions generated
with --forecast (since 1.30), but not to transactions recorded in the journal.
This means you can use `~` (periodic transaction) and `=` (auto posting) rules
together to generate forecast transactions, and when such a transaction actually occurs,
you can save the generated entry to the journal, finalising it.
The `=` directive declares a rule for automatically adding If instead you want to apply auto posting rules to recorded transactions
temporary extra postings (visible in reports, not in the journal file) as well, then use the `--auto` flag.
to all transactions matched by a certain query, This is not the default behaviour because depending on generated data
when you use the `--auto` flag. is not ideal for financial records (it's less portable, less future-proof,
less auditable, and less robust, since other features like balance assertions
Downsides: depending on generated data for your reports makes will be affected by the use or non-use of `--auto`.)
your financial data less portable, less future-proof,
and less trustworthy in an audit. Also, because the feature
is optional, other features like balance assertions can break
depending on whether it is on or off.
An auto posting rule looks a bit like a transaction: An auto posting rule looks a bit like a transaction:
```journal ```journal
@ -2562,6 +2561,14 @@ Also, any transaction that has been changed by auto posting rules will have thes
- `modified:` - this transaction was modified - `modified:` - this transaction was modified
- `_modified:` - a hidden tag not appearing in the comment; this transaction was modified "just now". - `_modified:` - a hidden tag not appearing in the comment; this transaction was modified "just now".
## Other syntax
hledger journal format supports quite a few other features,
mainly to make interoperating with or converting from Ledger easier.
Note some of the features below are powerful and can be useful in special cases,
but in general, features in this section are considered less important
or even not recommended for most users.
Downsides are mentioned to help you decide if you want to use them.
### Balance assignments ### Balance assignments
@ -4985,6 +4992,12 @@ When --forecast is not doing what you expect, one of these tips should help:
- Consult [Forecast period, in detail](#forecast-period-in-detail), above. - Consult [Forecast period, in detail](#forecast-period-in-detail), above.
- Check inside the engine: add `--debug=2` (eg). - Check inside the engine: add `--debug=2` (eg).
## Forecast and auto postings
Forecast transactions have one more feature: when they are generated,
any applicable [auto posting rules](#auto-postings) will also be applied to them,
generating additional postings. These are described below.
# Budgeting # Budgeting
With the balance command's [`--budget` report](#budget-report), With the balance command's [`--budget` report](#budget-report),

View File

@ -241,7 +241,7 @@ $ hledger -f- print --auto
# #
## Transaction modifiers affect forecast transactions (#959) ## Transaction modifiers affect only forecast transactions by default:
< <
= ^income = ^income
(liabilities:tax) *.33 ; income tax (liabilities:tax) *.33 ; income tax
@ -251,15 +251,15 @@ $ hledger -f- print --auto
income:donations $-15 income:donations $-15
assets:bank assets:bank
2016/1/3 withdraw 2016/1/3
assets:cash $20 assets:cash $100
assets:bank income:gifts
# 13. # 13.
$ hledger print -f- --auto --forecast -b 2016-01 -e 2016-03 $ hledger print -f- --forecast -b 2016-01 -e 2016-03
2016-01-03 withdraw 2016-01-03
assets:cash $20 assets:cash $100
assets:bank income:gifts
2016-02-01 paycheck 2016-02-01 paycheck
; generated-transaction: ~ monthly from 2016-01, modified: ; generated-transaction: ~ monthly from 2016-01, modified:
@ -271,16 +271,19 @@ $ hledger print -f- --auto --forecast -b 2016-01 -e 2016-03
>= >=
# 14. and they don't force --auto on # 14. With --auto, they affect all transactions:
$ hledger print -f- --forecast -b 2016-01 -e 2016-03 $ hledger print -f- --auto --forecast -b 2016-01 -e 2016-03
2016-01-03 withdraw 2016-01-03 ; modified:
assets:cash $20 assets:cash $100
assets:bank income:gifts
(liabilities:tax) $-33 ; income tax, generated-posting: = ^income
2016-02-01 paycheck 2016-02-01 paycheck
; generated-transaction: ~ monthly from 2016-01 ; generated-transaction: ~ monthly from 2016-01, modified:
income:remuneration $-100 income:remuneration $-100
(liabilities:tax) $-33 ; income tax, generated-posting: = ^income
income:donations $-15 income:donations $-15
(liabilities:tax) $-4.95 ; income tax, generated-posting: = ^income
assets:bank assets:bank
>= >=