REF don't use so many fields
This commit is contained in:
parent
53e1dde60f
commit
047e9edbb9
|
@ -128,6 +128,30 @@ insertBudget Budget {budgetLabel = name, income = is, transfers = es} = do
|
||||||
let bals = balanceTransfers txs
|
let bals = balanceTransfers txs
|
||||||
concat <$> mapM insertBudgetTx bals
|
concat <$> mapM insertBudgetTx bals
|
||||||
|
|
||||||
|
balanceTransfers :: [BudgetTxType] -> [BudgetTx]
|
||||||
|
balanceTransfers ts = snd $ L.mapAccumR go initBals $ L.sortOn (btWhen . bttTx) ts
|
||||||
|
where
|
||||||
|
initBals =
|
||||||
|
M.fromList $
|
||||||
|
fmap (,0) $
|
||||||
|
L.nub $
|
||||||
|
(fmap (bsAcnt . btTo . bttTx) ts ++ fmap (bsAcnt . btTo . bttTx) ts)
|
||||||
|
updateBal x = M.update (Just . (+ x))
|
||||||
|
lookupBal = M.findWithDefault (error "this should not happen")
|
||||||
|
go bals btt =
|
||||||
|
let tx = bttTx btt
|
||||||
|
from = bsAcnt $ btFrom tx
|
||||||
|
to = bsAcnt $ btTo tx
|
||||||
|
bal = lookupBal to bals
|
||||||
|
x = amtToMove bal (bttType btt) (btValue tx)
|
||||||
|
in (updateBal x to $ updateBal (-x) from bals, tx {btValue = x})
|
||||||
|
-- TODO might need to query signs to make this intuitive; as it is this will
|
||||||
|
-- probably work, but for credit accounts I might need to supply a negative
|
||||||
|
-- target value
|
||||||
|
amtToMove _ FixedAmt x = x
|
||||||
|
amtToMove bal Percent x = -(x / 100 * bal)
|
||||||
|
amtToMove bal Target x = x - bal
|
||||||
|
|
||||||
-- TODO allow currency conversions here
|
-- TODO allow currency conversions here
|
||||||
data BudgetSplit b = BudgetSplit
|
data BudgetSplit b = BudgetSplit
|
||||||
{ bsAcnt :: !AcntID
|
{ bsAcnt :: !AcntID
|
||||||
|
@ -136,33 +160,30 @@ data BudgetSplit b = BudgetSplit
|
||||||
|
|
||||||
data BudgetMeta = BudgetMeta
|
data BudgetMeta = BudgetMeta
|
||||||
{ bmCommit :: !(Key CommitR)
|
{ bmCommit :: !(Key CommitR)
|
||||||
, bmWhen :: !Day
|
|
||||||
, bmCur :: !CurID
|
, bmCur :: !CurID
|
||||||
, bmName :: !T.Text
|
, bmName :: !T.Text
|
||||||
}
|
}
|
||||||
|
|
||||||
data BudgetTx = BudgetTx
|
data BudgetTx = BudgetTx
|
||||||
{ btMeta :: !BudgetMeta
|
{ btMeta :: !BudgetMeta
|
||||||
|
, btWhen :: !Day
|
||||||
, btFrom :: !(BudgetSplit IncomeBucket)
|
, btFrom :: !(BudgetSplit IncomeBucket)
|
||||||
, btTo :: !(BudgetSplit ExpenseBucket)
|
, btTo :: !(BudgetSplit ExpenseBucket)
|
||||||
, btValue :: !Rational
|
, btValue :: !Rational
|
||||||
, btDesc :: !T.Text
|
, btDesc :: !T.Text
|
||||||
}
|
}
|
||||||
|
|
||||||
data TransferTx = TransferTx
|
data BudgetTxType = BudgetTxType
|
||||||
{ trxMeta :: !BudgetMeta
|
{ bttType :: !AmountType
|
||||||
, trxFrom :: !(BudgetSplit IncomeBucket)
|
, bttTx :: !BudgetTx
|
||||||
, trxTo :: !(BudgetSplit ExpenseBucket)
|
|
||||||
, trxValue :: !Rational
|
|
||||||
, trxType :: AmountType
|
|
||||||
, trxDesc :: !T.Text
|
|
||||||
}
|
}
|
||||||
|
|
||||||
insertIncome :: MonadFinance m => T.Text -> Income -> SqlPersistT m (EitherErrs [TransferTx])
|
insertIncome :: MonadFinance m => T.Text -> Income -> SqlPersistT m (EitherErrs [BudgetTxType])
|
||||||
insertIncome
|
insertIncome
|
||||||
name
|
name
|
||||||
i@Income {incWhen, incCurrency, incFrom, incPretax, incPosttax, incTaxes, incToBal} =
|
i@Income {incWhen, incCurrency, incFrom, incPretax, incPosttax, incTaxes, incToBal} =
|
||||||
whenHash CTIncome i (Right []) $ \c -> do
|
whenHash CTIncome i (Right []) $ \c -> do
|
||||||
|
let meta = BudgetMeta c incCurrency name
|
||||||
let balRes = balanceIncome i
|
let balRes = balanceIncome i
|
||||||
fromRes <- lift $ checkAcntType IncomeT incFrom (\j -> BudgetSplit j . Just)
|
fromRes <- lift $ checkAcntType IncomeT incFrom (\j -> BudgetSplit j . Just)
|
||||||
toRes <- lift $ expandTarget incToBal
|
toRes <- lift $ expandTarget incToBal
|
||||||
|
@ -171,78 +192,76 @@ insertIncome
|
||||||
Right (balance, fromFun, to) ->
|
Right (balance, fromFun, to) ->
|
||||||
fmap (fmap (concat . concat)) $
|
fmap (fmap (concat . concat)) $
|
||||||
withDates incWhen $ \day -> do
|
withDates incWhen $ \day -> do
|
||||||
let meta = BudgetMeta c day incCurrency name
|
|
||||||
let fromAllos b =
|
let fromAllos b =
|
||||||
fmap (fmap concat . concatEitherL)
|
fmap (fmap concat . concatEitherL)
|
||||||
. mapM (lift . fromAllo meta (fromFun b))
|
. mapM (lift . fromAllo day meta (fromFun b))
|
||||||
pre <- fromAllos PreTax incPretax
|
pre <- fromAllos PreTax incPretax
|
||||||
tax <-
|
tax <-
|
||||||
concatEitherL
|
concatEitherL
|
||||||
<$> mapM (lift . fromTax meta (fromFun IntraTax)) incTaxes
|
<$> mapM (lift . fromTax day meta (fromFun IntraTax)) incTaxes
|
||||||
post <- fromAllos PostTax incPosttax
|
post <- fromAllos PostTax incPosttax
|
||||||
let bal =
|
let bal =
|
||||||
TransferTx
|
BudgetTxType
|
||||||
{ trxMeta = meta
|
{ bttTx =
|
||||||
, trxFrom = fromFun PostTax
|
BudgetTx
|
||||||
, trxTo = to
|
{ btMeta = meta
|
||||||
, trxValue = balance
|
, btWhen = day
|
||||||
, trxType = FixedAmt
|
, btFrom = fromFun PostTax
|
||||||
, trxDesc = "balance after deductions"
|
, btTo = to
|
||||||
|
, btValue = balance
|
||||||
|
, btDesc = "balance after deductions"
|
||||||
|
}
|
||||||
|
, bttType = FixedAmt
|
||||||
}
|
}
|
||||||
return $ concatEithersL [Right [bal], tax, pre, post]
|
return $ concatEithersL [Right [bal], tax, pre, post]
|
||||||
|
|
||||||
fromAllo
|
fromAllo
|
||||||
:: MonadFinance m
|
:: MonadFinance m
|
||||||
=> BudgetMeta
|
=> Day
|
||||||
|
-> BudgetMeta
|
||||||
-> BudgetSplit IncomeBucket
|
-> BudgetSplit IncomeBucket
|
||||||
-> Allocation
|
-> Allocation
|
||||||
-> m (EitherErr [TransferTx])
|
-> m (EitherErr [BudgetTxType])
|
||||||
fromAllo meta from Allocation {alloPath, alloAmts} = do
|
fromAllo day meta from Allocation {alloPath, alloAmts} = do
|
||||||
-- TODO this is going to be repeated a zillion times (might matter)
|
-- TODO this is going to be repeated a zillion times (might matter)
|
||||||
res <- expandTarget alloPath
|
res <- expandTarget alloPath
|
||||||
return $ (\to -> fmap (toBT to) alloAmts) <$> res
|
return $ (\to -> fmap (toBT to) alloAmts) <$> res
|
||||||
where
|
where
|
||||||
toBT to (Amount desc v) =
|
toBT to (Amount desc v) =
|
||||||
TransferTx
|
BudgetTxType
|
||||||
{ trxFrom = from
|
{ bttTx =
|
||||||
, trxTo = to
|
BudgetTx
|
||||||
, trxValue = dec2Rat v
|
{ btFrom = from
|
||||||
, trxDesc = desc
|
, btWhen = day
|
||||||
, trxType = FixedAmt
|
, btTo = to
|
||||||
, trxMeta = meta
|
, btValue = dec2Rat v
|
||||||
|
, btDesc = desc
|
||||||
|
, btMeta = meta
|
||||||
|
}
|
||||||
|
, bttType = FixedAmt
|
||||||
}
|
}
|
||||||
|
|
||||||
expandTarget :: MonadFinance m => TransferTarget -> m (EitherErr (BudgetSplit ExpenseBucket))
|
|
||||||
expandTarget t = case t of
|
|
||||||
ExpenseTarget i b -> checkAcntType ExpenseT i $ (`BudgetSplit` (Just b))
|
|
||||||
GenericTarget i -> checkAcntTypes (LiabilityT :| [AssetT, EquityT]) i $ (`BudgetSplit` Nothing)
|
|
||||||
|
|
||||||
checkAcntType :: MonadFinance m => AcntType -> AcntID -> (AcntID -> a) -> m (EitherErr a)
|
|
||||||
checkAcntType t = checkAcntTypes (t :| [])
|
|
||||||
|
|
||||||
checkAcntTypes :: MonadFinance m => NE.NonEmpty AcntType -> AcntID -> (AcntID -> a) -> m (EitherErr a)
|
|
||||||
checkAcntTypes ts i f = (go =<<) <$> lookupAccountType i
|
|
||||||
where
|
|
||||||
go t
|
|
||||||
| t `L.elem` ts = Right $ f i
|
|
||||||
| otherwise = Left $ AccountError i t
|
|
||||||
|
|
||||||
fromTax
|
fromTax
|
||||||
:: MonadFinance m
|
:: MonadFinance m
|
||||||
=> BudgetMeta
|
=> Day
|
||||||
|
-> BudgetMeta
|
||||||
-> BudgetSplit IncomeBucket
|
-> BudgetSplit IncomeBucket
|
||||||
-> Tax
|
-> Tax
|
||||||
-> m (EitherErr TransferTx)
|
-> m (EitherErr BudgetTxType)
|
||||||
fromTax meta from Tax {taxAcnt = to, taxValue = v} =
|
fromTax day meta from Tax {taxAcnt = to, taxValue = v} =
|
||||||
-- TODO this is going to be repeated a zillion times (might matter)
|
-- TODO this is going to be repeated a zillion times (might matter)
|
||||||
checkAcntType ExpenseT to $ \to_ ->
|
checkAcntType ExpenseT to $ \to_ ->
|
||||||
TransferTx
|
BudgetTxType
|
||||||
{ trxFrom = from
|
{ bttTx =
|
||||||
, trxTo = BudgetSplit to_ (Just Fixed)
|
BudgetTx
|
||||||
, trxValue = dec2Rat v
|
{ btFrom = from
|
||||||
, trxDesc = ""
|
, btWhen = day
|
||||||
, trxType = FixedAmt
|
, btTo = BudgetSplit to_ (Just Fixed)
|
||||||
, trxMeta = meta
|
, btValue = dec2Rat v
|
||||||
|
, btDesc = ""
|
||||||
|
, btMeta = meta
|
||||||
|
}
|
||||||
|
, bttType = FixedAmt
|
||||||
}
|
}
|
||||||
|
|
||||||
balanceIncome :: Income -> EitherErr Rational
|
balanceIncome :: Income -> EitherErr Rational
|
||||||
|
@ -265,43 +284,16 @@ sumAllocation = sum . fmap (dec2Rat . amtValue) . alloAmts
|
||||||
sumTaxes :: [Tax] -> Rational
|
sumTaxes :: [Tax] -> Rational
|
||||||
sumTaxes = sum . fmap (dec2Rat . taxValue)
|
sumTaxes = sum . fmap (dec2Rat . taxValue)
|
||||||
|
|
||||||
expandTransfers :: MonadFinance m => T.Text -> [Transfer] -> SqlPersistT m (EitherErrs [TransferTx])
|
expandTransfers
|
||||||
|
:: MonadFinance m
|
||||||
|
=> T.Text
|
||||||
|
-> [Transfer]
|
||||||
|
-> SqlPersistT m (EitherErrs [BudgetTxType])
|
||||||
expandTransfers name ts = do
|
expandTransfers name ts = do
|
||||||
txs <- mapM (expandTransfer name) ts
|
txs <- mapM (expandTransfer name) ts
|
||||||
return $ L.sortOn (bmWhen . trxMeta) . concat <$> concatEithersL txs
|
return $ L.sortOn (btWhen . bttTx) . concat <$> concatEithersL txs
|
||||||
|
|
||||||
balanceTransfers :: [TransferTx] -> [BudgetTx]
|
expandTransfer :: MonadFinance m => T.Text -> Transfer -> SqlPersistT m (EitherErrs [BudgetTxType])
|
||||||
balanceTransfers ts = snd $ L.mapAccumR go initBals $ L.sortOn (bmWhen . trxMeta) ts
|
|
||||||
where
|
|
||||||
initBals =
|
|
||||||
M.fromList $
|
|
||||||
fmap (,0) $
|
|
||||||
L.nub $
|
|
||||||
(fmap (bsAcnt . trxTo) ts ++ fmap (bsAcnt . trxTo) ts)
|
|
||||||
updateBal x = M.update (Just . (+ x))
|
|
||||||
lookupBal = M.findWithDefault (error "this should not happen")
|
|
||||||
go bals TransferTx {trxMeta, trxFrom, trxTo, trxValue, trxType, trxDesc} =
|
|
||||||
let from = bsAcnt trxFrom
|
|
||||||
to = bsAcnt trxTo
|
|
||||||
bal = lookupBal to bals
|
|
||||||
x = amtToMove bal trxType trxValue
|
|
||||||
t =
|
|
||||||
BudgetTx
|
|
||||||
{ btMeta = trxMeta
|
|
||||||
, btFrom = trxFrom
|
|
||||||
, btTo = trxTo
|
|
||||||
, btValue = x
|
|
||||||
, btDesc = trxDesc
|
|
||||||
}
|
|
||||||
in (updateBal x to $ updateBal (-x) from bals, t)
|
|
||||||
-- TODO might need to query signs to make this intuitive; as it is this will
|
|
||||||
-- probably work, but for credit accounts I might need to supply a negative
|
|
||||||
-- target value
|
|
||||||
amtToMove _ FixedAmt x = x
|
|
||||||
amtToMove bal Percent x = -(x / 100 * bal)
|
|
||||||
amtToMove bal Target x = x - bal
|
|
||||||
|
|
||||||
expandTransfer :: MonadFinance m => T.Text -> Transfer -> SqlPersistT m (EitherErrs [TransferTx])
|
|
||||||
expandTransfer name t@Transfer {transAmounts, transTo, transCurrency, transFrom} =
|
expandTransfer name t@Transfer {transAmounts, transTo, transCurrency, transFrom} =
|
||||||
whenHash CTExpense t (Right []) $ \key ->
|
whenHash CTExpense t (Right []) $ \key ->
|
||||||
fmap (fmap concat . concatEithersL) $
|
fmap (fmap concat . concatEithersL) $
|
||||||
|
@ -313,27 +305,30 @@ expandTransfer name t@Transfer {transAmounts, transTo, transCurrency, transFrom}
|
||||||
Right to -> withDates pat $ \day ->
|
Right to -> withDates pat $ \day ->
|
||||||
let meta =
|
let meta =
|
||||||
BudgetMeta
|
BudgetMeta
|
||||||
{ bmWhen = day
|
{ bmCur = transCurrency
|
||||||
, bmCur = transCurrency
|
|
||||||
, bmCommit = key
|
, bmCommit = key
|
||||||
, bmName = name
|
, bmName = name
|
||||||
}
|
}
|
||||||
tx =
|
tx =
|
||||||
TransferTx
|
BudgetTxType
|
||||||
{ trxMeta = meta
|
{ bttTx =
|
||||||
, trxFrom = BudgetSplit transFrom Nothing
|
BudgetTx
|
||||||
, trxTo = to
|
{ btMeta = meta
|
||||||
, trxValue = dec2Rat v
|
, btWhen = day
|
||||||
, trxType = atype
|
, btFrom = BudgetSplit transFrom Nothing
|
||||||
, trxDesc = desc
|
, btTo = to
|
||||||
|
, btValue = dec2Rat v
|
||||||
|
, btDesc = desc
|
||||||
|
}
|
||||||
|
, bttType = atype
|
||||||
}
|
}
|
||||||
in return $ Right tx
|
in return $ Right tx
|
||||||
|
|
||||||
insertBudgetTx :: MonadFinance m => BudgetTx -> SqlPersistT m [InsertError]
|
insertBudgetTx :: MonadFinance m => BudgetTx -> SqlPersistT m [InsertError]
|
||||||
insertBudgetTx BudgetTx {btFrom, btTo, btMeta, btValue, btDesc} = do
|
insertBudgetTx BudgetTx {btFrom, btTo, btMeta, btValue, btDesc, btWhen} = do
|
||||||
res <- lift $ splitPair (bsAcnt btFrom) (bsAcnt btTo) (bmCur btMeta) btValue
|
res <- lift $ splitPair (bsAcnt btFrom) (bsAcnt btTo) (bmCur btMeta) btValue
|
||||||
unlessLefts_ res $ \(sFrom, sTo) -> do
|
unlessLefts_ res $ \(sFrom, sTo) -> do
|
||||||
k <- insert $ TransactionR (bmCommit btMeta) (bmWhen btMeta) btDesc
|
k <- insert $ TransactionR (bmCommit btMeta) btWhen btDesc
|
||||||
insertBudgetLabel name k IncomeBucketR sFrom btFrom
|
insertBudgetLabel name k IncomeBucketR sFrom btFrom
|
||||||
insertBudgetLabel name k ExpenseBucketR sTo btTo
|
insertBudgetLabel name k ExpenseBucketR sTo btTo
|
||||||
where
|
where
|
||||||
|
@ -373,6 +368,36 @@ splitPair from to cur val = do
|
||||||
, sCurrency = cur
|
, sCurrency = cur
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expandTarget
|
||||||
|
:: MonadFinance m
|
||||||
|
=> TransferTarget
|
||||||
|
-> m (EitherErr (BudgetSplit ExpenseBucket))
|
||||||
|
expandTarget t = case t of
|
||||||
|
ExpenseTarget i b -> checkAcntType ExpenseT i $ (`BudgetSplit` (Just b))
|
||||||
|
GenericTarget i ->
|
||||||
|
checkAcntTypes (LiabilityT :| [AssetT, EquityT]) i $
|
||||||
|
(`BudgetSplit` Nothing)
|
||||||
|
|
||||||
|
checkAcntType
|
||||||
|
:: MonadFinance m
|
||||||
|
=> AcntType
|
||||||
|
-> AcntID
|
||||||
|
-> (AcntID -> a)
|
||||||
|
-> m (EitherErr a)
|
||||||
|
checkAcntType t = checkAcntTypes (t :| [])
|
||||||
|
|
||||||
|
checkAcntTypes
|
||||||
|
:: MonadFinance m
|
||||||
|
=> NE.NonEmpty AcntType
|
||||||
|
-> AcntID
|
||||||
|
-> (AcntID -> a)
|
||||||
|
-> m (EitherErr a)
|
||||||
|
checkAcntTypes ts i f = (go =<<) <$> lookupAccountType i
|
||||||
|
where
|
||||||
|
go t
|
||||||
|
| t `L.elem` ts = Right $ f i
|
||||||
|
| otherwise = Left $ AccountError i t
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- statements
|
-- statements
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue