FIX rounding errors
This commit is contained in:
parent
dce3ff4166
commit
24bc9a239b
14
app/Main.hs
14
app/Main.hs
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue