close: add the --interleaved flag; refactor a little

Interleaved equity postings make troubleshooting easier.
This commit is contained in:
Simon Michael 2020-01-18 05:00:55 -08:00
parent a5ff9d6a63
commit 1e9f30bafc
3 changed files with 91 additions and 27 deletions

View File

@ -27,14 +27,18 @@ closemode = hledgerCommandMode
,flagNone ["opening"] (setboolopt "opening") "show just opening transaction" ,flagNone ["opening"] (setboolopt "opening") "show just opening transaction"
,flagReq ["close-to"] (\s opts -> Right $ setopt "close-to" s opts) "ACCT" ("account to transfer closing balances to (default: "++defclosingacct++")") ,flagReq ["close-to"] (\s opts -> Right $ setopt "close-to" s opts) "ACCT" ("account to transfer closing balances to (default: "++defclosingacct++")")
,flagReq ["open-from"] (\s opts -> Right $ setopt "open-from" s opts) "ACCT" ("account to transfer opening balances from (default: "++defopeningacct++")") ,flagReq ["open-from"] (\s opts -> Right $ setopt "open-from" s opts) "ACCT" ("account to transfer opening balances from (default: "++defopeningacct++")")
,flagNone ["interleaved"] (setboolopt "interleaved") "keep equity and non-equity postings adjacent"
] ]
[generalflagsgroup1] [generalflagsgroup1]
hiddenflags hiddenflags
([], Just $ argsFlag "[QUERY]") ([], Just $ argsFlag "[QUERY]")
-- debugger, beware: close is incredibly devious. simple rules combine to make a horrid maze.
close CliOpts{rawopts_=rawopts, reportopts_=ropts} j = do close CliOpts{rawopts_=rawopts, reportopts_=ropts} j = do
today <- getCurrentDay today <- getCurrentDay
let let
-- interleave equity postings next to the corresponding closing posting, or put them all at the end ?
interleaved = boolopt "interleaved" rawopts
(opening, closing) = (opening, closing) =
case (boolopt "opening" rawopts, boolopt "closing" rawopts) of case (boolopt "opening" rawopts, boolopt "closing" rawopts) of
(False, False) -> (True, True) -- by default show both opening and closing (False, False) -> (True, True) -- by default show both opening and closing
@ -46,51 +50,78 @@ close CliOpts{rawopts_=rawopts, reportopts_=ropts} j = do
openingdate = fromMaybe today $ queryEndDate False q openingdate = fromMaybe today $ queryEndDate False q
closingdate = addDays (-1) openingdate closingdate = addDays (-1) openingdate
(acctbals,_) = balanceReportFromMultiBalanceReport ropts_ q j (acctbals,_) = balanceReportFromMultiBalanceReport ropts_ q j
balancingamt = negate $ sum $ map (\(_,_,_,b) -> normaliseMixedAmount b) acctbals balancingamt = sum $ map (\(_,_,_,b) -> normaliseMixedAmount b) acctbals
-- since balance assertion amounts are required to be exact, the -- since balance assertion amounts are required to be exact, the
-- amounts in opening/closing transactions should be too (#941, #1137) -- amounts in opening/closing transactions should be too (#941, #1137)
setprec = setFullPrecision setprec = setFullPrecision
-- balance assertion amounts will be unpriced (#824) closingps = concat
-- only the last posting in each commodity will have a balance assertion (#1035) [[posting{paccount = a
closingps = [posting{paccount = a ,pamount = mixed [setprec $ negate b]
,pamount = mixed [setprec $ negate b] -- after each commodity's last posting, assert 0 balance (#1035)
,pbalanceassertion = if islast then Just nullassertion{baamount=setprec b{aquantity=0, aprice=Nothing}} else Nothing -- balance assertion amounts are unpriced (#824)
} ,pbalanceassertion =
| (a,_,_,mb) <- acctbals if islast
-- the balances in each commodity, and for each transaction price then Just nullassertion{baamount=setprec b{aquantity=0, aprice=Nothing}}
, let bs = amounts $ normaliseMixedAmount mb else Nothing
-- mark the last balance in each commodity }
, let bs' = concat [reverse $ zip (reverse bs) (True : repeat False) ] ++
| bs <- groupBy ((==) `on` acommodity) bs] if interleaved then
, (b, islast) <- bs' -- a corresponding posting transferring the above balance to equity
[posting{paccount = closingacct
,pamount = Mixed [b]
}
]
else []
| (a,_,_,mb) <- acctbals
-- the balances in each commodity, and for each transaction price
, let bs = amounts $ normaliseMixedAmount mb
-- mark the last balance in each commodity
, let bs' = concat [reverse $ zip (reverse bs) (True : repeat False)
| bs <- groupBy ((==) `on` acommodity) bs]
, (b, islast) <- bs'
] ]
-- The balancing posting to equity. Allow this one to have a multicommodity amount,
-- and don't try to assert its balance.
++ ++
if interleaved then []
else
-- a final posting transferring all the balances to equity
-- (print will show it as multiple single-commodity postings)
[posting{paccount = closingacct [posting{paccount = closingacct
,pamount = negate balancingamt ,pamount = balancingamt
} }
] ]
openingps = [posting{paccount = a openingps = concat
[[posting{paccount = a
,pamount = mixed [setprec b] ,pamount = mixed [setprec b]
,pbalanceassertion = case mcommoditysum of ,pbalanceassertion = case mcommoditysum of
Just s -> Just nullassertion{baamount=setprec s{aprice=Nothing}} Just s -> Just nullassertion{baamount=setprec s{aprice=Nothing}}
Nothing -> Nothing Nothing -> Nothing
} }
| (a,_,_,mb) <- acctbals ] ++
-- the balances in each commodity, and for each transaction price if interleaved then
, let bs = amounts $ normaliseMixedAmount mb -- a corresponding posting transferring the above balance from equity
-- mark the last balance in each commodity, with the unpriced sum in that commodity [posting{paccount = openingacct
, let bs' = concat [reverse $ zip (reverse bs) (Just commoditysum : repeat Nothing) ,pamount = Mixed [negate b]
| bs <- groupBy ((==) `on` acommodity) bs }
, let commoditysum = (sum bs)] ]
, (b, mcommoditysum) <- bs' else []
| (a,_,_,mb) <- acctbals
-- the balances in each commodity, and for each transaction price
, let bs = amounts $ normaliseMixedAmount mb
-- mark the last balance in each commodity, with the unpriced sum in that commodity
, let bs' = concat [reverse $ zip (reverse bs) (Just commoditysum : repeat Nothing)
| bs <- groupBy ((==) `on` acommodity) bs
, let commoditysum = (sum bs)]
, (b, mcommoditysum) <- bs'
] ]
++ ++
if interleaved then []
else
-- a final posting transferring all the balances from equity
-- (print will show it as multiple single-commodity postings)
[posting{paccount = openingacct [posting{paccount = openingacct
,pamount = balancingamt ,pamount = negate balancingamt
} }
] ]

View File

@ -13,6 +13,9 @@ or you can customise these with the `--close-to` and `--open-from` options.
You can choose to print just one of the transactions by using the You can choose to print just one of the transactions by using the
`--opening` or `--closing` flag. `--opening` or `--closing` flag.
The equity postings appear at the end of the transaction by default;
with `--interleaved`, they appear beside their corresponding closing postings.
If you split your journal files by time (eg yearly), you will If you split your journal files by time (eg yearly), you will
typically run this command at the end of the year, and save the typically run this command at the end of the year, and save the
closing transaction as last entry of the old file, and the opening closing transaction as last entry of the old file, and the opening

View File

@ -237,3 +237,33 @@ $ hledger -f- close -p 2016 assets liabilities
>=0 >=0
# 9. With --interleaved, balanced posting pairs are adjacent.
# (And balances with the same cost are not necessarily combined into
# a single posting. Eg the 5734 EUR above is 5733 EUR and 1 EUR below.)
$ hledger -f- close -p 2016 assets liabilities --interleaved
2016-12-31 closing balances
assets:bank -5,733.00 EUR = 0.00 EUR
equity:closing balances 5,733.00 EUR
liabilities:employer $-10,000.00
equity:closing balances $10,000.00
liabilities:employer $5,000.00 @ 0.93 EUR
equity:closing balances $-5,000.00 @ 0.93 EUR
liabilities:employer $5,000.00 @ 0.95 EUR = $0.00
equity:closing balances $-5,000.00 @ 0.95 EUR
liabilities:employer -1.00 EUR = 0.00 EUR
equity:closing balances 1.00 EUR
2017-01-01 opening balances
assets:bank 5,733.00 EUR = 5,733.00 EUR
equity:opening balances -5,733.00 EUR
liabilities:employer $10,000.00
equity:opening balances $-10,000.00
liabilities:employer $-5,000.00 @ 0.93 EUR
equity:opening balances $5,000.00 @ 0.93 EUR
liabilities:employer $-5,000.00 @ 0.95 EUR = $0.00
equity:opening balances $5,000.00 @ 0.95 EUR
liabilities:employer 1.00 EUR = 1.00 EUR
equity:opening balances -1.00 EUR
>=0