FIX rounding errors

This commit is contained in:
Nathan Dwarshuis 2023-07-06 00:05:16 -04:00
parent dce3ff4166
commit 24bc9a239b
4 changed files with 55 additions and 32 deletions

View File

@ -178,17 +178,31 @@ runSync c = do
flip runReaderT state $ do flip runReaderT state $ do
let (hTs, hSs) = splitHistory $ statements config let (hTs, hSs) = splitHistory $ statements config
hSs' <- mapErrorsIO (readHistStmt root) hSs hSs' <- mapErrorsIO (readHistStmt root) hSs
-- lift $ print $ length $ lefts hSs'
-- lift $ print $ length $ rights hSs'
hTs' <- liftIOExceptT $ mapErrors readHistTransfer hTs hTs' <- liftIOExceptT $ mapErrors readHistTransfer hTs
-- lift $ print $ length $ lefts hTs'
bTs <- liftIOExceptT $ mapErrors readBudget $ budget config bTs <- liftIOExceptT $ mapErrors readBudget $ budget config
-- lift $ print $ length $ lefts bTs
return $ second concat $ partitionEithers $ hSs' ++ hTs' ++ bTs return $ second concat $ partitionEithers $ hSs' ++ hTs' ++ bTs
-- print $ length $ kmNewCommits state
-- print $ length $ duOldCommits updates
-- print $ length $ duNewTagIds updates
-- print $ length $ duNewAcntPaths updates
-- print $ length $ duNewAcntIds updates
-- print $ length $ duNewCurrencyIds updates
-- Update the DB. -- Update the DB.
runSqlQueryT pool $ withTransaction $ flip runReaderT state $ do runSqlQueryT pool $ withTransaction $ flip runReaderT state $ do
-- NOTE this must come first (unless we defer foreign keys) -- NOTE this must come first (unless we defer foreign keys)
updateDBState updates updateDBState updates
-- TODO skip this entire section if the database won't change (eg length
-- of 'is' is zero and there are no commits to delete)
res <- runExceptT $ do res <- runExceptT $ do
-- TODO taking out the hash is dumb -- TODO taking out the hash is dumb
(rs, ues) <- readUpdates $ fmap commitRHash rus (rs, ues) <- readUpdates $ fmap commitRHash rus
-- rerunnableIO $ print ues
-- rerunnableIO $ print $ length rs
let ebs = fmap ToUpdate ues ++ fmap ToRead rs ++ fmap ToInsert is let ebs = fmap ToUpdate ues ++ fmap ToRead rs ++ fmap ToInsert is
insertAll ebs insertAll ebs
-- NOTE this rerunnable thing is a bit misleading; fromEither will throw -- NOTE this rerunnable thing is a bit misleading; fromEither will throw

View File

@ -428,7 +428,7 @@ readUpdates
-> m ([ReadEntry], [Either TotalUpdateEntrySet FullUpdateEntrySet]) -> m ([ReadEntry], [Either TotalUpdateEntrySet FullUpdateEntrySet])
readUpdates hashes = do readUpdates hashes = do
xs <- selectE $ do xs <- selectE $ do
(commits :& txs :& entrysets :& entries) <- (commits :& txs :& entrysets :& entries :& currencies) <-
E.from E.from
$ E.table @CommitR $ E.table @CommitR
`E.innerJoin` E.table @TransactionR `E.innerJoin` E.table @TransactionR
@ -437,6 +437,8 @@ readUpdates hashes = do
`E.on` (\(_ :& t :& es) -> t ^. TransactionRId ==. es ^. EntrySetRTransaction) `E.on` (\(_ :& t :& es) -> t ^. TransactionRId ==. es ^. EntrySetRTransaction)
`E.innerJoin` E.table @EntryR `E.innerJoin` E.table @EntryR
`E.on` (\(_ :& _ :& es :& e) -> es ^. EntrySetRId ==. e ^. EntryREntryset) `E.on` (\(_ :& _ :& es :& e) -> es ^. EntrySetRId ==. e ^. EntryREntryset)
`E.innerJoin` E.table @CurrencyR
`E.on` (\(_ :& _ :& es :& _ :& cur) -> es ^. EntrySetRCurrency ==. cur ^. CurrencyRId)
E.where_ $ commits ^. CommitRHash `E.in_` E.valList hashes E.where_ $ commits ^. CommitRHash `E.in_` E.valList hashes
return return
( entrysets ^. EntrySetRRebalance ( entrysets ^. EntrySetRRebalance
@ -445,7 +447,10 @@ readUpdates hashes = do
( entrysets ^. EntrySetRId ( entrysets ^. EntrySetRId
, txs ^. TransactionRDate , txs ^. TransactionRDate
, txs ^. TransactionRBudgetName , txs ^. TransactionRBudgetName
, entrysets ^. EntrySetRCurrency ,
( entrysets ^. EntrySetRCurrency
, currencies ^. CurrencyRPrecision
)
) )
, entries , entries
) )
@ -454,7 +459,7 @@ readUpdates hashes = do
toUpdate' <- liftExcept $ mapErrors makeUES $ groupKey (\(i, _, _, _) -> i) (snd <$> toUpdate) toUpdate' <- liftExcept $ mapErrors makeUES $ groupKey (\(i, _, _, _) -> i) (snd <$> toUpdate)
return (makeRE . snd <$> toRead, toUpdate') return (makeRE . snd <$> toRead, toUpdate')
where where
makeUES ((_, day, name, curID), es) = do makeUES ((_, day, name, (curID, prec)), es) = do
let res = let res =
bimap NE.nonEmpty NE.nonEmpty $ bimap NE.nonEmpty NE.nonEmpty $
NE.partition ((< 0) . entryRIndex . snd) $ NE.partition ((< 0) . entryRIndex . snd) $
@ -471,7 +476,7 @@ readUpdates hashes = do
Left $ Left $
UpdateEntrySet UpdateEntrySet
{ utDate = E.unValue day { utDate = E.unValue day
, utCurrency = E.unValue curID , utCurrency = (E.unValue curID, fromIntegral $ E.unValue prec)
, utFrom0 = x , utFrom0 = x
, utTo0 = to0 , utTo0 = to0
, utFromRO = fromRO , utFromRO = fromRO
@ -485,7 +490,7 @@ readUpdates hashes = do
Right $ Right $
UpdateEntrySet UpdateEntrySet
{ utDate = E.unValue day { utDate = E.unValue day
, utCurrency = E.unValue curID , utCurrency = (E.unValue curID, fromIntegral $ E.unValue prec)
, utFrom0 = x , utFrom0 = x
, utTo0 = to0 , utTo0 = to0
, utFromRO = fromRO , utFromRO = fromRO
@ -496,7 +501,7 @@ readUpdates hashes = do
, utBudget = E.unValue name , utBudget = E.unValue name
} }
_ -> throwError undefined _ -> throwError undefined
makeRE ((_, day, name, curID), entry) = makeRE ((_, day, name, (curID, _)), entry) =
let e = entityVal entry let e = entityVal entry
in ReadEntry in ReadEntry
{ reDate = E.unValue day { reDate = E.unValue day
@ -671,7 +676,7 @@ insertTx c InsertTx {itxDate, itxDescr, itxEntrySets, itxBudget} = do
-- BudgetCommit _ name -> insert_ $ BudgetLabelR ek name -- BudgetCommit _ name -> insert_ $ BudgetLabelR ek name
-- _ -> return () -- _ -> return ()
insertEntry :: MonadSqlQuery m => EntrySetRId -> Int -> KeyEntry -> m EntryRId insertEntry :: MonadSqlQuery m => EntrySetRId -> Int -> InsertEntry -> m EntryRId
insertEntry insertEntry
k k
i i

View File

@ -120,7 +120,7 @@ data UpdateEntrySet f t = UpdateEntrySet
, utToUnk :: ![UEUnk] , utToUnk :: ![UEUnk]
, utFromRO :: ![UE_RO] , utFromRO :: ![UE_RO]
, utToRO :: ![UE_RO] , utToRO :: ![UE_RO]
, utCurrency :: !CurrencyRId , utCurrency :: !(CurrencyRId, Natural)
, utDate :: !Day , utDate :: !Day
, utTotalValue :: !t , utTotalValue :: !t
, utBudget :: !T.Text , utBudget :: !T.Text

View File

@ -733,19 +733,19 @@ rebalanceTotalEntrySet
, utToUnk , utToUnk
, utFromRO , utFromRO
, utToRO , utToRO
, utCurrency , utCurrency = (curID, precision)
, utTotalValue , utTotalValue
, utBudget , utBudget
} = } =
do do
(fval, fs, tpairs) <- rebalanceDebit bc utFromRO utFromUnk (fval, fs, tpairs) <- rebalanceDebit bc precision utFromRO utFromUnk
let f0val = utTotalValue - fval let f0val = utTotalValue - fval
modify $ mapAdd_ (f0Acnt, bc) f0val modify $ mapAdd_ (f0Acnt, bc) f0val
let tsLinked = tpairs ++ (unlink f0val <$> f0links) let tsLinked = tpairs ++ (unlink f0val <$> f0links)
ts <- rebalanceCredit bc utTotalValue utTo0 utToUnk utToRO tsLinked ts <- rebalanceCredit bc precision utTotalValue utTo0 utToUnk utToRO tsLinked
return (f0 {ueValue = StaticValue f0val} : fs ++ ts) return (f0 {ueValue = StaticValue f0val} : fs ++ ts)
where where
bc = (utCurrency, utBudget) bc = (curID, utBudget)
rebalanceFullEntrySet :: FullUpdateEntrySet -> State EntryBals [UEBalanced] rebalanceFullEntrySet :: FullUpdateEntrySet -> State EntryBals [UEBalanced]
rebalanceFullEntrySet rebalanceFullEntrySet
@ -756,25 +756,26 @@ rebalanceFullEntrySet
, utToUnk , utToUnk
, utFromRO , utFromRO
, utToRO , utToRO
, utCurrency , utCurrency = (curID, precision)
, utBudget , utBudget
} = } =
do do
(ftot, fs, tpairs) <- rebalanceDebit bc rs ls (ftot, fs, tpairs) <- rebalanceDebit bc precision rs ls
ts <- rebalanceCredit bc ftot utTo0 utToUnk utToRO tpairs ts <- rebalanceCredit bc precision ftot utTo0 utToUnk utToRO tpairs
return (fs ++ ts) return (fs ++ ts)
where where
(rs, ls) = case utFrom0 of (rs, ls) = case utFrom0 of
Left x -> (x : utFromRO, utFromUnk) Left x -> (x : utFromRO, utFromUnk)
Right x -> (utFromRO, x : utFromUnk) Right x -> (utFromRO, x : utFromUnk)
bc = (utCurrency, utBudget) bc = (curID, utBudget)
rebalanceDebit rebalanceDebit
:: BCKey :: BCKey
-> Natural
-> [UE_RO] -> [UE_RO]
-> [(UEUnk, [UELink])] -> [(UEUnk, [UELink])]
-> State EntryBals (Rational, [UEBalanced], [UEBalanced]) -> State EntryBals (Rational, [UEBalanced], [UEBalanced])
rebalanceDebit k ro linked = do rebalanceDebit k precision ro linked = do
(tot, (tpairs, fs)) <- (tot, (tpairs, fs)) <-
fmap (second (partitionEithers . concat)) $ fmap (second (partitionEithers . concat)) $
sumM goFrom $ sumM goFrom $
@ -785,7 +786,7 @@ rebalanceDebit k ro linked = do
idx = either ueIndex (ueIndex . fst) idx = either ueIndex (ueIndex . fst)
goFrom (Left e) = (,[]) <$> updateFixed k e goFrom (Left e) = (,[]) <$> updateFixed k e
goFrom (Right (e0, es)) = do goFrom (Right (e0, es)) = do
v <- updateUnknown k e0 v <- updateUnknown precision k e0
let e0' = Right $ e0 {ueValue = StaticValue v} let e0' = Right $ e0 {ueValue = StaticValue v}
let es' = Left . unlink v <$> es let es' = Left . unlink v <$> es
return (v, e0' : es') return (v, e0' : es')
@ -795,13 +796,14 @@ unlink v e = e {ueValue = StaticValue $ (-v) * unLinkScale (ueValue e)}
rebalanceCredit rebalanceCredit
:: BCKey :: BCKey
-> Natural
-> Rational -> Rational
-> UEBlank -> UEBlank
-> [UEUnk] -> [UEUnk]
-> [UE_RO] -> [UE_RO]
-> [UEBalanced] -> [UEBalanced]
-> State EntryBals [UEBalanced] -> State EntryBals [UEBalanced]
rebalanceCredit k tot t0 us rs bs = do rebalanceCredit k precision tot t0 us rs bs = do
(tval, ts) <- (tval, ts) <-
fmap (second catMaybes) $ fmap (second catMaybes) $
sumM goTo $ sumM goTo $
@ -815,7 +817,7 @@ rebalanceCredit k tot t0 us rs bs = do
goTo (UETReadOnly e) = (,Nothing) <$> updateFixed k e goTo (UETReadOnly e) = (,Nothing) <$> updateFixed k e
goTo (UETLinked e) = (,Just e) <$> updateFixed k e goTo (UETLinked e) = (,Just e) <$> updateFixed k e
goTo (UETUnk e) = do goTo (UETUnk e) = do
v <- updateUnknown k e v <- updateUnknown precision k e
return (v, Just $ e {ueValue = StaticValue v}) return (v, Just $ e {ueValue = StaticValue v})
data UpdateEntryType a b data UpdateEntryType a b
@ -834,11 +836,11 @@ updateFixed k e = do
modify $ mapAdd_ (ueAcnt e, k) v modify $ mapAdd_ (ueAcnt e, k) v
return v return v
updateUnknown :: BCKey -> UpdateEntry i EntryValueUnk -> State EntryBals Rational updateUnknown :: Natural -> BCKey -> UpdateEntry i EntryValueUnk -> State EntryBals Rational
updateUnknown k e = do updateUnknown precision k e = do
let key = (ueAcnt e, k) let key = (ueAcnt e, k)
curBal <- gets (M.findWithDefault 0 key) curBal <- gets (M.findWithDefault 0 key)
let v = case ueValue e of let v = roundPrecision precision $ fromRational $ case ueValue e of
EVPercent p -> p * curBal EVPercent p -> p * curBal
EVBalance p -> p - curBal EVBalance p -> p - curBal
modify $ mapAdd_ key v modify $ mapAdd_ key v
@ -868,7 +870,7 @@ balancePrimaryEntrySet
let bc = (curID, budgetName) let bc = (curID, budgetName)
combineErrorM (combineError f0res fsres (,)) (combineError t0res tsres (,)) $ combineErrorM (combineError f0res fsres (,)) (combineError t0res tsres (,)) $
\(f0', fs') (t0', ts') -> do \(f0', fs') (t0', ts') -> do
let balFrom = fmap liftInnerS . balanceDeferred let balFrom = fmap liftInnerS . balanceDeferred precision
fs'' <- doEntries balFrom bc esTotalValue f0' fs' fs'' <- doEntries balFrom bc esTotalValue f0' fs'
balanceFinal bc (-esTotalValue) precision fs'' t0' ts' balanceFinal bc (-esTotalValue) precision fs'' t0' ts'
@ -894,7 +896,7 @@ balanceSecondaryEntrySet
balanceFinal bc (-tot) precision fs'' t0' ts' balanceFinal bc (-tot) precision fs'' t0' ts'
where where
entrySum = sum . fmap (eValue . ieEntry) entrySum = sum . fmap (eValue . ieEntry)
balFrom = balanceEntry (fmap liftInnerS . balanceDeferred) bc balFrom = balanceEntry (fmap liftInnerS . balanceDeferred precision) bc
bc = (curID, budgetName) bc = (curID, budgetName)
balanceFinal balanceFinal
@ -958,16 +960,17 @@ balanceLinked from precision k lg = case lg of
-- TODO this error would be much more informative if I had access to the -- TODO this error would be much more informative if I had access to the
-- file from which it came -- file from which it came
Nothing -> throwError undefined Nothing -> throwError undefined
(LinkDeferred d) -> liftInnerS $ balanceDeferred k d (LinkDeferred d) -> liftInnerS $ balanceDeferred precision k d
where where
go s = negate . roundPrecision precision . (* s) . fromRational go s = negate . roundPrecision precision . (* s) . fromRational
balanceDeferred balanceDeferred
:: ABCKey :: Natural
-> ABCKey
-> EntryValue Rational -> EntryValue Rational
-> State EntryBals (Rational, Maybe DBDeferred) -> State EntryBals (Rational, Maybe DBDeferred)
balanceDeferred k (EntryValue t v) = do balanceDeferred prec k (EntryValue t v) = do
newval <- findBalance k t v newval <- findBalance prec k t v
let d = case t of let d = case t of
TFixed -> Nothing TFixed -> Nothing
TBalance -> Just $ EntryBalance v TBalance -> Just $ EntryBalance v
@ -1001,13 +1004,14 @@ resolveAcntAndTags e@Entry {eAcnt, eTags} = do
\(acntID, sign, _) tags -> e {eAcnt = (acntID, sign), eTags = tags} \(acntID, sign, _) tags -> e {eAcnt = (acntID, sign), eTags = tags}
findBalance findBalance
:: ABCKey :: Natural
-> ABCKey
-> TransferType -> TransferType
-> Rational -> Rational
-> State EntryBals Rational -> State EntryBals Rational
findBalance k t v = do findBalance prec k t v = do
curBal <- gets (M.findWithDefault 0 k) curBal <- gets (M.findWithDefault 0 k)
return $ case t of return $ roundPrecision prec $ fromRational $ case t of
TBalance -> v - curBal TBalance -> v - curBal
TPercent -> v * curBal TPercent -> v * curBal
TFixed -> v TFixed -> v