diff --git a/hledger/Hledger/Cli/Commands/Close.hs b/hledger/Hledger/Cli/Commands/Close.hs index d8a02e6db..dfe662529 100755 --- a/hledger/Hledger/Cli/Commands/Close.hs +++ b/hledger/Hledger/Cli/Commands/Close.hs @@ -27,14 +27,18 @@ closemode = hledgerCommandMode ,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 ["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] hiddenflags ([], 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 today <- getCurrentDay let + -- interleave equity postings next to the corresponding closing posting, or put them all at the end ? + interleaved = boolopt "interleaved" rawopts (opening, closing) = case (boolopt "opening" rawopts, boolopt "closing" rawopts) of (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 closingdate = addDays (-1) openingdate (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 -- amounts in opening/closing transactions should be too (#941, #1137) setprec = setFullPrecision - -- balance assertion amounts will be unpriced (#824) - -- only the last posting in each commodity will have a balance assertion (#1035) - closingps = [posting{paccount = a - ,pamount = mixed [setprec $ negate b] - ,pbalanceassertion = if islast then Just nullassertion{baamount=setprec b{aquantity=0, aprice=Nothing}} else Nothing - } - | (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' + closingps = concat + [[posting{paccount = a + ,pamount = mixed [setprec $ negate b] + -- after each commodity's last posting, assert 0 balance (#1035) + -- balance assertion amounts are unpriced (#824) + ,pbalanceassertion = + if islast + then Just nullassertion{baamount=setprec b{aquantity=0, aprice=Nothing}} + else Nothing + } + ] ++ + if interleaved then + -- 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 - ,pamount = negate balancingamt + ,pamount = balancingamt } ] - openingps = [posting{paccount = a + openingps = concat + [[posting{paccount = a ,pamount = mixed [setprec b] ,pbalanceassertion = case mcommoditysum of Just s -> Just nullassertion{baamount=setprec s{aprice=Nothing}} Nothing -> Nothing } - | (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 + -- a corresponding posting transferring the above balance from equity + [posting{paccount = openingacct + ,pamount = Mixed [negate 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, 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 - ,pamount = balancingamt + ,pamount = negate balancingamt } ] diff --git a/hledger/Hledger/Cli/Commands/Close.md b/hledger/Hledger/Cli/Commands/Close.md index 765117089..6229f657a 100644 --- a/hledger/Hledger/Cli/Commands/Close.md +++ b/hledger/Hledger/Cli/Commands/Close.md @@ -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 `--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 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 diff --git a/tests/close.test b/tests/close.test index 93c13555e..de94a8f95 100644 --- a/tests/close.test +++ b/tests/close.test @@ -237,3 +237,33 @@ $ hledger -f- close -p 2016 assets liabilities >=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 +