close: preserve transaction prices (costs) accurately (#1035)
Transaction prices were being collapsed/misreported after close/open; this is fixed. Now each separately-priced amount gets its own posting, and only the last of these (for each commodity) gets a balance assertion. Also the equity posting's amount is now always shown explicitly, which in multicommodity situations means that multiple equity postings are shown. The upshot is that a balance -B report will be unchanged after closing & opening transactions.
This commit is contained in:
parent
3059a0b2ca
commit
6c2398e4d7
@ -8,6 +8,8 @@ module Hledger.Cli.Commands.Close (
|
|||||||
where
|
where
|
||||||
|
|
||||||
import Control.Monad (when)
|
import Control.Monad (when)
|
||||||
|
import Data.Function (on)
|
||||||
|
import Data.List (groupBy)
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import Data.Time.Calendar
|
import Data.Time.Calendar
|
||||||
import System.Console.CmdArgs.Explicit as C
|
import System.Console.CmdArgs.Explicit as C
|
||||||
@ -19,6 +21,7 @@ closemode = hledgerCommandMode
|
|||||||
$(embedFileRelative "Hledger/Cli/Commands/Close.txt")
|
$(embedFileRelative "Hledger/Cli/Commands/Close.txt")
|
||||||
[flagNone ["opening"] (setboolopt "opening") "show just opening transaction"
|
[flagNone ["opening"] (setboolopt "opening") "show just opening transaction"
|
||||||
,flagNone ["closing"] (setboolopt "closing") "show just closing transaction"
|
,flagNone ["closing"] (setboolopt "closing") "show just closing transaction"
|
||||||
|
-- ,flagNone ["explicit","x"] (setboolopt "explicit") "show all amounts explicitly"
|
||||||
]
|
]
|
||||||
[generalflagsgroup1]
|
[generalflagsgroup1]
|
||||||
hiddenflags
|
hiddenflags
|
||||||
@ -36,31 +39,61 @@ 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) -> normaliseMixedAmountSquashPricesForDisplay b) acctbals
|
balancingamt = negate $ 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)
|
-- amounts in opening/closing transactions should be too (#941)
|
||||||
-- setprec = setFullPrecision
|
-- setprec = setFullPrecision
|
||||||
setprec = setNaturalPrecision
|
setprec = setNaturalPrecision
|
||||||
-- balance assertion amounts will be unpriced, cf #824
|
-- balance assertion amounts will be unpriced (#824)
|
||||||
closingps = [posting{paccount=a
|
-- only the last posting in each commodity will have a balance assertion (#1035)
|
||||||
,pamount=mixed [setprec $ negate b]
|
closingps = [posting{paccount = a
|
||||||
,pbalanceassertion=Just assertion{baamount=setprec b{aquantity=0, aprice=Nothing}}
|
,pamount = mixed [setprec $ negate b]
|
||||||
|
,pbalanceassertion = if islast then Just assertion{baamount=setprec b{aquantity=0, aprice=Nothing}} else Nothing
|
||||||
}
|
}
|
||||||
| (a,_,_,mb) <- acctbals
|
| (a,_,_,mb) <- acctbals
|
||||||
, b <- amounts $ normaliseMixedAmountSquashPricesForDisplay mb
|
-- 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.
|
||||||
|
++
|
||||||
|
[posting{paccount = "equity:closing balances"
|
||||||
|
,pamount = negate balancingamt
|
||||||
|
}
|
||||||
]
|
]
|
||||||
++ [posting{paccount="equity:closing balances", pamount=negate balancingamt}]
|
|
||||||
|
|
||||||
openingps = [posting{paccount=a
|
openingps = [posting{paccount = a
|
||||||
,pamount=mixed [setprec b]
|
,pamount = mixed [setprec b]
|
||||||
,pbalanceassertion=Just assertion{baamount=setprec b{aprice=Nothing}}
|
,pbalanceassertion = case mcommoditysum of
|
||||||
|
Just s -> Just assertion{baamount=setprec s{aprice=Nothing}}
|
||||||
|
Nothing -> Nothing
|
||||||
}
|
}
|
||||||
| (a,_,_,mb) <- acctbals
|
| (a,_,_,mb) <- acctbals
|
||||||
, b <- amounts $ normaliseMixedAmountSquashPricesForDisplay mb
|
-- 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'
|
||||||
|
]
|
||||||
|
++
|
||||||
|
[posting{paccount = "equity:opening balances"
|
||||||
|
,pamount = balancingamt
|
||||||
|
}
|
||||||
]
|
]
|
||||||
++ [posting{paccount="equity:opening balances", pamount=balancingamt}]
|
|
||||||
|
|
||||||
when closing $ putStr $ showTransaction (nulltransaction{tdate=closingdate, tdescription="closing balances", tpostings=closingps})
|
-- With -x, show all amounts explicitly (ie, also in the balancing equity posting(s)).
|
||||||
when opening $ putStr $ showTransaction (nulltransaction{tdate=openingdate, tdescription="opening balances", tpostings=openingps})
|
-- print also does it for -B; I think that isn't needed here.
|
||||||
|
-- showtxn | boolopt "explicit" rawopts = showTransactionUnelided
|
||||||
|
-- | otherwise = showTransaction
|
||||||
|
showtxn = showTransactionUnelided
|
||||||
|
|
||||||
|
when closing $ putStr $ showtxn (nulltransaction{tdate=closingdate, tdescription="closing balances", tpostings=closingps})
|
||||||
|
when opening $ putStr $ showtxn (nulltransaction{tdate=openingdate, tdescription="opening balances", tpostings=openingps})
|
||||||
|
|
||||||
|
|||||||
@ -42,6 +42,10 @@ the generated balance assertions will depend on these flags.
|
|||||||
Likewise, if you run this command with --auto, the balance assertions
|
Likewise, if you run this command with --auto, the balance assertions
|
||||||
will probably always require --auto.
|
will probably always require --auto.
|
||||||
|
|
||||||
|
When account balances have cost information (transaction prices), the
|
||||||
|
closing/opening transactions will preserve it, so that eg balance -B reports
|
||||||
|
will not be affected.
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
Carrying asset/liability balances into a new file for 2019, all from command line:
|
Carrying asset/liability balances into a new file for 2019, all from command line:
|
||||||
|
|||||||
161
tests/close.test
161
tests/close.test
@ -23,53 +23,17 @@ $ hledger close -f- -p 2016 assets liabilities
|
|||||||
assets:bank $-80 = $0
|
assets:bank $-80 = $0
|
||||||
assets:cash $-10 = $0
|
assets:cash $-10 = $0
|
||||||
liabilities $-25 = $0
|
liabilities $-25 = $0
|
||||||
equity:closing balances
|
equity:closing balances $115
|
||||||
|
|
||||||
2017/01/01 opening balances
|
2017/01/01 opening balances
|
||||||
assets:bank $80 = $80
|
assets:bank $80 = $80
|
||||||
assets:cash $10 = $10
|
assets:cash $10 = $10
|
||||||
liabilities $25 = $25
|
liabilities $25 = $25
|
||||||
equity:opening balances
|
equity:opening balances $-115
|
||||||
|
|
||||||
>=0
|
>=0
|
||||||
|
|
||||||
# 2. Test aggregation of postings with prices
|
# 2. A begin date should be ignored
|
||||||
<
|
|
||||||
Y2016
|
|
||||||
01/31
|
|
||||||
liabilities:employer $5,000.00
|
|
||||||
income:salary
|
|
||||||
|
|
||||||
02/05
|
|
||||||
liabilities:employer -$5,000.00 @ 0.95 EUR
|
|
||||||
expenses:tax 1,852.50 EUR
|
|
||||||
assets:bank 2,897.00 EUR
|
|
||||||
liabilities:employer
|
|
||||||
|
|
||||||
02/29
|
|
||||||
liabilities:employer $5,000.00
|
|
||||||
income:salary
|
|
||||||
|
|
||||||
03/04
|
|
||||||
liabilities:employer -$5,000.0 @ 0.93 EUR
|
|
||||||
expenses:tax 1,813.50 EUR
|
|
||||||
assets:bank 2,836.00 EUR
|
|
||||||
liabilities:employer
|
|
||||||
|
|
||||||
$ hledger close -f- -p 2016 assets liabilities
|
|
||||||
2016/12/31 closing balances
|
|
||||||
assets:bank -5,733 EUR = 0 EUR
|
|
||||||
liabilities:employer -1 EUR = 0 EUR
|
|
||||||
equity:closing balances
|
|
||||||
|
|
||||||
2017/01/01 opening balances
|
|
||||||
assets:bank 5,733 EUR = 5,733 EUR
|
|
||||||
liabilities:employer 1 EUR = 1 EUR
|
|
||||||
equity:opening balances
|
|
||||||
|
|
||||||
>=0
|
|
||||||
|
|
||||||
# 3. A begin date should be ignored
|
|
||||||
<
|
<
|
||||||
2017/1/1
|
2017/1/1
|
||||||
(a) 1
|
(a) 1
|
||||||
@ -77,15 +41,15 @@ $ hledger close -f- -p 2016 assets liabilities
|
|||||||
$ hledger close -f- -b2017/6/1 -e2018
|
$ hledger close -f- -b2017/6/1 -e2018
|
||||||
2017/12/31 closing balances
|
2017/12/31 closing balances
|
||||||
a -1 = 0
|
a -1 = 0
|
||||||
equity:closing balances
|
equity:closing balances 1
|
||||||
|
|
||||||
2018/01/01 opening balances
|
2018/01/01 opening balances
|
||||||
a 1 = 1
|
a 1 = 1
|
||||||
equity:opening balances
|
equity:opening balances -1
|
||||||
|
|
||||||
>=0
|
>=0
|
||||||
|
|
||||||
# 4. Print just the opening transaction
|
# 3. Print just the opening transaction
|
||||||
<
|
<
|
||||||
2016/1/1 open
|
2016/1/1 open
|
||||||
assets:bank $100
|
assets:bank $100
|
||||||
@ -109,11 +73,11 @@ $ hledger close -f- -p 2016 assets liabilities --opening
|
|||||||
assets:bank $80 = $80
|
assets:bank $80 = $80
|
||||||
assets:cash $10 = $10
|
assets:cash $10 = $10
|
||||||
liabilities $25 = $25
|
liabilities $25 = $25
|
||||||
equity:opening balances
|
equity:opening balances $-115
|
||||||
|
|
||||||
>=0
|
>=0
|
||||||
|
|
||||||
# 5. Print just the closing transaction
|
# 4. Print just the closing transaction
|
||||||
<
|
<
|
||||||
2016/1/1 open
|
2016/1/1 open
|
||||||
assets:bank $100
|
assets:bank $100
|
||||||
@ -137,11 +101,11 @@ $ hledger close -f- -p 2016 assets liabilities --closing
|
|||||||
assets:bank $-80 = $0
|
assets:bank $-80 = $0
|
||||||
assets:cash $-10 = $0
|
assets:cash $-10 = $0
|
||||||
liabilities $-25 = $0
|
liabilities $-25 = $0
|
||||||
equity:closing balances
|
equity:closing balances $115
|
||||||
|
|
||||||
>=0
|
>=0
|
||||||
|
|
||||||
# 6. Supplying --opening --closing is the same as just "close"
|
# 5. Supplying --opening --closing is the same as just "close"
|
||||||
<
|
<
|
||||||
2016/1/1 open
|
2016/1/1 open
|
||||||
assets:bank $100
|
assets:bank $100
|
||||||
@ -165,12 +129,113 @@ $ hledger close -f- -p 2016 assets liabilities --opening --closing
|
|||||||
assets:bank $-80 = $0
|
assets:bank $-80 = $0
|
||||||
assets:cash $-10 = $0
|
assets:cash $-10 = $0
|
||||||
liabilities $-25 = $0
|
liabilities $-25 = $0
|
||||||
equity:closing balances
|
equity:closing balances $115
|
||||||
|
|
||||||
2017/01/01 opening balances
|
2017/01/01 opening balances
|
||||||
assets:bank $80 = $80
|
assets:bank $80 = $80
|
||||||
assets:cash $10 = $10
|
assets:cash $10 = $10
|
||||||
liabilities $25 = $25
|
liabilities $25 = $25
|
||||||
equity:opening balances
|
equity:opening balances $-115
|
||||||
|
|
||||||
|
>=0
|
||||||
|
|
||||||
|
# 6. Closing a multi-priced balance. The "lot" prices are preserved.
|
||||||
|
# Only the last posting in each commodity gets a balance assertion (#1035).
|
||||||
|
# Balance assertion amounts do not have a price.
|
||||||
|
<
|
||||||
|
2019/01/01
|
||||||
|
assets 1A @ 1B
|
||||||
|
assets 1A @ 1C
|
||||||
|
equity
|
||||||
|
|
||||||
|
$ hledger -f- close assets -p 2019
|
||||||
|
2019/12/31 closing balances
|
||||||
|
assets -1A @ 1B
|
||||||
|
assets -1A @ 1C = 0A
|
||||||
|
equity:closing balances 1A @ 1B
|
||||||
|
equity:closing balances 1A @ 1C
|
||||||
|
|
||||||
|
2020/01/01 opening balances
|
||||||
|
assets 1A @ 1B
|
||||||
|
assets 1A @ 1C = 2A
|
||||||
|
equity:opening balances -1A @ 1B
|
||||||
|
equity:opening balances -1A @ 1C
|
||||||
|
|
||||||
|
>=0
|
||||||
|
|
||||||
|
# 7. Closing a multi-priced balance, slightly more complex
|
||||||
|
# (different price in each transaction). Hopefully
|
||||||
|
# equivalent to 8.
|
||||||
|
<
|
||||||
|
2019/01/01
|
||||||
|
(assets) 1A @ 1B
|
||||||
|
|
||||||
|
2019/01/02
|
||||||
|
(assets) 1A @ 2B
|
||||||
|
|
||||||
|
$ hledger -f- close assets -p 2019
|
||||||
|
2019/12/31 closing balances
|
||||||
|
assets -1A @ 1B
|
||||||
|
assets -1A @ 2B = 0A
|
||||||
|
equity:closing balances 1A @ 1B
|
||||||
|
equity:closing balances 1A @ 2B
|
||||||
|
|
||||||
|
2020/01/01 opening balances
|
||||||
|
assets 1A @ 1B
|
||||||
|
assets 1A @ 2B = 2A
|
||||||
|
equity:opening balances -1A @ 1B
|
||||||
|
equity:opening balances -1A @ 2B
|
||||||
|
|
||||||
|
>=0
|
||||||
|
|
||||||
|
# 8. Closing a multi-priced balance, a more complex example.
|
||||||
|
<
|
||||||
|
2016/01/31
|
||||||
|
liabilities:employer $5,000.00
|
||||||
|
income:salary
|
||||||
|
|
||||||
|
2016/02/05
|
||||||
|
liabilities:employer $-5,000.00 @ 0.95 EUR
|
||||||
|
expenses:tax 1,852.50 EUR
|
||||||
|
assets:bank 2,897.00 EUR
|
||||||
|
liabilities:employer
|
||||||
|
|
||||||
|
2016/02/29
|
||||||
|
liabilities:employer $5,000.00
|
||||||
|
income:salary
|
||||||
|
|
||||||
|
2016/03/04
|
||||||
|
liabilities:employer $-5,000.00 @ 0.93 EUR
|
||||||
|
expenses:tax 1,813.50 EUR
|
||||||
|
assets:bank 2,836.00 EUR
|
||||||
|
liabilities:employer
|
||||||
|
|
||||||
|
; Note: without these declarations, the closing/opening entries below
|
||||||
|
; would cause decimal marks to be misparsed. (How ?)
|
||||||
|
;commodity $1,000.00
|
||||||
|
;commodity 1,000.00 EUR
|
||||||
|
|
||||||
|
$ hledger -f- close -p 2016 assets liabilities
|
||||||
|
2016/12/31 closing balances
|
||||||
|
assets:bank -5,733 EUR = 0 EUR
|
||||||
|
liabilities:employer $-10,000
|
||||||
|
liabilities:employer $5,000 @ 0.93 EUR
|
||||||
|
liabilities:employer $5,000 @ 0.95 EUR = $0
|
||||||
|
liabilities:employer -1 EUR = 0 EUR
|
||||||
|
equity:closing balances $10,000.00
|
||||||
|
equity:closing balances $-5,000.00 @ 0.93 EUR
|
||||||
|
equity:closing balances $-5,000.00 @ 0.95 EUR
|
||||||
|
equity:closing balances 5,734.00 EUR
|
||||||
|
|
||||||
|
2017/01/01 opening balances
|
||||||
|
assets:bank 5,733 EUR = 5,733 EUR
|
||||||
|
liabilities:employer $10,000
|
||||||
|
liabilities:employer $-5,000 @ 0.93 EUR
|
||||||
|
liabilities:employer $-5,000 @ 0.95 EUR = $0
|
||||||
|
liabilities:employer 1 EUR = 1 EUR
|
||||||
|
equity:opening balances $-10,000.00
|
||||||
|
equity:opening balances $5,000.00 @ 0.93 EUR
|
||||||
|
equity:opening balances $5,000.00 @ 0.95 EUR
|
||||||
|
equity:opening balances -5,734.00 EUR
|
||||||
|
|
||||||
>=0
|
>=0
|
||||||
|
|||||||
@ -365,23 +365,7 @@ hledger -f- print --explicit
|
|||||||
|
|
||||||
>>>=0
|
>>>=0
|
||||||
|
|
||||||
# 21. close generates balance assertions without prices
|
# 21. The exact amounts are compared; display precision does not affect assertions.
|
||||||
hledger -f- close -e 2019/1/2
|
|
||||||
<<<
|
|
||||||
2019/01/01
|
|
||||||
(a) 1A @ 1B = 1A @ 2B
|
|
||||||
>>>
|
|
||||||
2019/01/01 closing balances
|
|
||||||
a -1A @ 1B = 0A
|
|
||||||
equity:closing balances
|
|
||||||
|
|
||||||
2019/01/02 opening balances
|
|
||||||
a 1A @ 1B = 1A
|
|
||||||
equity:opening balances
|
|
||||||
|
|
||||||
>>>=0
|
|
||||||
|
|
||||||
# 22. The exact amounts are compared; display precision does not affect assertions.
|
|
||||||
hledger -f- print
|
hledger -f- print
|
||||||
<<<
|
<<<
|
||||||
commodity $1000.00
|
commodity $1000.00
|
||||||
@ -396,7 +380,7 @@ commodity $1000.00
|
|||||||
>>>2
|
>>>2
|
||||||
>>>=0
|
>>>=0
|
||||||
|
|
||||||
# 23. This fails
|
# 22. This fails
|
||||||
hledger -f- print
|
hledger -f- print
|
||||||
<<<
|
<<<
|
||||||
commodity $1000.00
|
commodity $1000.00
|
||||||
@ -410,7 +394,7 @@ commodity $1000.00
|
|||||||
>>>2 /difference: 0\.004/
|
>>>2 /difference: 0\.004/
|
||||||
>>>=1
|
>>>=1
|
||||||
|
|
||||||
# 24. This fails
|
# 23. This fails
|
||||||
hledger -f- print
|
hledger -f- print
|
||||||
<<<
|
<<<
|
||||||
commodity $1000.00
|
commodity $1000.00
|
||||||
@ -424,7 +408,7 @@ commodity $1000.00
|
|||||||
>>>2 /difference: 0\.0001/
|
>>>2 /difference: 0\.0001/
|
||||||
>>>=1
|
>>>=1
|
||||||
|
|
||||||
# 25. Inclusive assertions include balances from subaccounts.
|
# 24. Inclusive assertions include balances from subaccounts.
|
||||||
hledger -f- print
|
hledger -f- print
|
||||||
<<<
|
<<<
|
||||||
2019/1/1
|
2019/1/1
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user