journal: inclusive balance assignments now work (#1207)

This commit is contained in:
Simon Michael 2020-03-05 13:38:19 -08:00
parent 41bb7865f3
commit 21fdcec6b7
2 changed files with 52 additions and 25 deletions

View File

@ -92,6 +92,7 @@ import Control.Monad.ST
import Data.Array.ST import Data.Array.ST
import Data.Default (Default(..)) import Data.Default (Default(..))
import Data.Function ((&)) import Data.Function ((&))
import qualified Data.HashTable.Class as H (toList)
import qualified Data.HashTable.ST.Cuckoo as H import qualified Data.HashTable.ST.Cuckoo as H
import Data.List import Data.List
import Data.List.Extra (groupSort, nubSort) import Data.List.Extra (groupSort, nubSort)
@ -632,13 +633,13 @@ data BalancingState s = BalancingState {
withRunningBalance :: (BalancingState s -> ST s a) -> Balancing s a withRunningBalance :: (BalancingState s -> ST s a) -> Balancing s a
withRunningBalance f = ask >>= lift . lift . f withRunningBalance f = ask >>= lift . lift . f
-- | Get this account's current running balance (exclusive). -- | Get this account's current exclusive running balance.
getRunningBalanceB :: AccountName -> Balancing s MixedAmount getRunningBalanceB :: AccountName -> Balancing s MixedAmount
getRunningBalanceB acc = withRunningBalance $ \BalancingState{bsBalances} -> do getRunningBalanceB acc = withRunningBalance $ \BalancingState{bsBalances} -> do
fromMaybe 0 <$> H.lookup bsBalances acc fromMaybe 0 <$> H.lookup bsBalances acc
-- | Add this amount to this account's running balance, -- | Add this amount to this account's exclusive running balance.
-- and return the new running balance (exclusive). -- Returns the new running balance.
addToRunningBalanceB :: AccountName -> MixedAmount -> Balancing s MixedAmount addToRunningBalanceB :: AccountName -> MixedAmount -> Balancing s MixedAmount
addToRunningBalanceB acc amt = withRunningBalance $ \BalancingState{bsBalances} -> do addToRunningBalanceB acc amt = withRunningBalance $ \BalancingState{bsBalances} -> do
old <- fromMaybe 0 <$> H.lookup bsBalances acc old <- fromMaybe 0 <$> H.lookup bsBalances acc
@ -646,14 +647,28 @@ addToRunningBalanceB acc amt = withRunningBalance $ \BalancingState{bsBalances}
H.insert bsBalances acc new H.insert bsBalances acc new
return new return new
-- | Set this account's running balance (exclusive) to this amount, -- | Set this account's exclusive running balance to this amount.
-- and return the difference from the previous value. -- Returns the change in exclusive running balance.
setRunningBalanceB :: AccountName -> MixedAmount -> Balancing s MixedAmount setRunningBalanceB :: AccountName -> MixedAmount -> Balancing s MixedAmount
setRunningBalanceB acc amt = withRunningBalance $ \BalancingState{bsBalances} -> do setRunningBalanceB acc amt = withRunningBalance $ \BalancingState{bsBalances} -> do
old <- fromMaybe 0 <$> H.lookup bsBalances acc old <- fromMaybe 0 <$> H.lookup bsBalances acc
H.insert bsBalances acc amt H.insert bsBalances acc amt
return $ amt - old return $ amt - old
-- | Set this account's exclusive running balance to whatever amount
-- makes its *inclusive* running balance (the sum of exclusive running
-- balances of this account and any subaccounts) be the given amount.
-- Returns the change in exclusive running balance.
setInclusiveRunningBalanceB :: AccountName -> MixedAmount -> Balancing s MixedAmount
setInclusiveRunningBalanceB acc newibal = withRunningBalance $ \BalancingState{bsBalances} -> do
oldebal <- fromMaybe 0 <$> H.lookup bsBalances acc
allebals <- H.toList bsBalances
let subsibal = -- sum of any subaccounts' running balances
sum $ map snd $ filter ((acc `isAccountNamePrefixOf`).fst) allebals
let newebal = newibal - subsibal
H.insert bsBalances acc newebal
return $ newebal - oldebal
-- | Update (overwrite) this transaction in the balancing state. -- | Update (overwrite) this transaction in the balancing state.
updateTransactionB :: Transaction -> Balancing s () updateTransactionB :: Transaction -> Balancing s ()
updateTransactionB t = withRunningBalance $ \BalancingState{bsTransactions} -> updateTransactionB t = withRunningBalance $ \BalancingState{bsTransactions} ->
@ -754,19 +769,19 @@ addOrAssignAmountAndCheckAssertionB p@Posting{paccount=acc, pamount=amt, pbalanc
-- no explicit posting amount, but there is a balance assignment -- no explicit posting amount, but there is a balance assignment
-- TODO this doesn't yet handle inclusive assignments right, #1207 -- TODO this doesn't yet handle inclusive assignments right, #1207
| Just BalanceAssertion{baamount,batotal} <- mba = do | Just BalanceAssertion{baamount,batotal,bainclusive} <- mba = do
(diff,newbal) <- case batotal of (diff,newbal) <- case batotal of
-- a total balance assignment (==, all commodities) -- a total balance assignment (==, all commodities)
True -> do True -> do
let newbal = Mixed [baamount] let newbal = Mixed [baamount]
diff <- setRunningBalanceB acc newbal diff <- (if bainclusive then setInclusiveRunningBalanceB else setRunningBalanceB) acc newbal
return (diff,newbal) return (diff,newbal)
-- a partial balance assignment (=, one commodity) -- a partial balance assignment (=, one commodity)
False -> do False -> do
oldbalothercommodities <- filterMixedAmount ((acommodity baamount /=) . acommodity) <$> getRunningBalanceB acc oldbalothercommodities <- filterMixedAmount ((acommodity baamount /=) . acommodity) <$> getRunningBalanceB acc
let assignedbalthiscommodity = Mixed [baamount] let assignedbalthiscommodity = Mixed [baamount]
newbal = oldbalothercommodities + assignedbalthiscommodity newbal = oldbalothercommodities + assignedbalthiscommodity
diff <- setRunningBalanceB acc newbal diff <- (if bainclusive then setInclusiveRunningBalanceB else setRunningBalanceB) acc newbal
return (diff,newbal) return (diff,newbal)
let p' = p{pamount=diff, poriginal=Just $ originalPosting p} let p' = p{pamount=diff, poriginal=Just $ originalPosting p}
whenM (R.reader bsAssrt) $ checkBalanceAssertionB p' newbal whenM (R.reader bsAssrt) $ checkBalanceAssertionB p' newbal

View File

@ -419,20 +419,32 @@ $ hledger -f- print
>=0 >=0
## 25. Inclusive balance assignments also work (#1207). # 25. Inclusive balance assignments also work (#1207).
#< <
#2020-01-25 2020-01-25
# (a:aa) 1 (a:aa) 1
#
#2020-01-31 2020-01-25
# (a) ==* 1 (a:bb) 1
#
#$ hledger -f- print -x 2020-01-25
#> (a) 1
#2020-01-25
# (a:aa) 1 2020-01-31
# (a) ==* 1
#2020-01-31
# (a) 0 ==* 1 $ hledger -f- print -x
# >
#>=0 2020-01-25
(a:aa) 1
2020-01-25
(a:bb) 1
2020-01-25
(a) 1
2020-01-31
(a) -2 ==* 1
>=0