ADD errors for everything that needs them (ish)
This commit is contained in:
parent
cafc066881
commit
2e0b591312
|
@ -255,7 +255,7 @@ runSync threads c bs hs = do
|
||||||
rerunnableIO $ fromEither res
|
rerunnableIO $ fromEither res
|
||||||
where
|
where
|
||||||
root = takeDirectory c
|
root = takeDirectory c
|
||||||
err (InsertException es) = do
|
err (AppException es) = do
|
||||||
liftIO $ mapM_ TI.putStrLn $ concatMap showError es
|
liftIO $ mapM_ TI.putStrLn $ concatMap showError es
|
||||||
exitFailure
|
exitFailure
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ import qualified RIO.NonEmpty as NE
|
||||||
import qualified RIO.Text as T
|
import qualified RIO.Text as T
|
||||||
import RIO.Time
|
import RIO.Time
|
||||||
|
|
||||||
readBudget :: (MonadInsertError m, MonadFinance m) => Budget -> m [Tx CommitR]
|
readBudget :: (MonadAppError m, MonadFinance m) => Budget -> m [Tx CommitR]
|
||||||
readBudget
|
readBudget
|
||||||
b@Budget
|
b@Budget
|
||||||
{ bgtLabel
|
{ bgtLabel
|
||||||
|
@ -56,7 +56,7 @@ readBudget
|
||||||
localSpan <- liftExcept $ resolveDaySpan bi
|
localSpan <- liftExcept $ resolveDaySpan bi
|
||||||
return $ intersectDaySpan globalSpan localSpan
|
return $ intersectDaySpan globalSpan localSpan
|
||||||
|
|
||||||
sortAllo :: MultiAllocation v -> InsertExcept (DaySpanAllocation v)
|
sortAllo :: MultiAllocation v -> AppExcept (DaySpanAllocation v)
|
||||||
sortAllo a@Allocation {alloAmts = as} = do
|
sortAllo a@Allocation {alloAmts = as} = do
|
||||||
bs <- foldSpan [] $ L.sortOn amtWhen as
|
bs <- foldSpan [] $ L.sortOn amtWhen as
|
||||||
return $ a {alloAmts = reverse bs}
|
return $ a {alloAmts = reverse bs}
|
||||||
|
@ -76,7 +76,7 @@ sortAllo a@Allocation {alloAmts = as} = do
|
||||||
-- iteration which is a total waste, but the fix requires turning this
|
-- iteration which is a total waste, but the fix requires turning this
|
||||||
-- loop into a fold which I don't feel like doing now :(
|
-- loop into a fold which I don't feel like doing now :(
|
||||||
readIncome
|
readIncome
|
||||||
:: (MonadInsertError m, MonadFinance m)
|
:: (MonadAppError m, MonadFinance m)
|
||||||
=> CommitR
|
=> CommitR
|
||||||
-> BudgetName
|
-> BudgetName
|
||||||
-> IntAllocations
|
-> IntAllocations
|
||||||
|
@ -104,7 +104,7 @@ readIncome
|
||||||
(combineError incRes nonIncRes (,))
|
(combineError incRes nonIncRes (,))
|
||||||
(combineError cpRes dayRes (,))
|
(combineError cpRes dayRes (,))
|
||||||
$ \_ (cp, days) -> do
|
$ \_ (cp, days) -> do
|
||||||
let gross = realFracToDecimal' (cpPrec cp) incGross
|
let gross = realFracToDecimalP (cpPrec cp) incGross
|
||||||
foldDays (allocate cp gross) start days
|
foldDays (allocate cp gross) start days
|
||||||
where
|
where
|
||||||
srcAcnt' = AcntID srcAcnt
|
srcAcnt' = AcntID srcAcnt
|
||||||
|
@ -163,7 +163,7 @@ periodScaler
|
||||||
:: PeriodType
|
:: PeriodType
|
||||||
-> Day
|
-> Day
|
||||||
-> Day
|
-> Day
|
||||||
-> InsertExcept PeriodScaler
|
-> AppExcept PeriodScaler
|
||||||
periodScaler pt prev cur = return scale
|
periodScaler pt prev cur = return scale
|
||||||
where
|
where
|
||||||
n = workingDays wds prev cur
|
n = workingDays wds prev cur
|
||||||
|
@ -172,10 +172,10 @@ periodScaler pt prev cur = return scale
|
||||||
Daily ds -> ds
|
Daily ds -> ds
|
||||||
scale prec x = case pt of
|
scale prec x = case pt of
|
||||||
Hourly HourlyPeriod {hpAnnualHours, hpDailyHours} ->
|
Hourly HourlyPeriod {hpAnnualHours, hpDailyHours} ->
|
||||||
realFracToDecimal' prec (x / fromIntegral hpAnnualHours)
|
realFracToDecimalP prec (x / fromIntegral hpAnnualHours)
|
||||||
* fromIntegral hpDailyHours
|
* fromIntegral hpDailyHours
|
||||||
* fromIntegral n
|
* fromIntegral n
|
||||||
Daily _ -> realFracToDecimal' prec (x * fromIntegral n / 365.25)
|
Daily _ -> realFracToDecimalP prec (x * fromIntegral n / 365.25)
|
||||||
|
|
||||||
-- ASSUME start < end
|
-- ASSUME start < end
|
||||||
workingDays :: [Weekday] -> Day -> Day -> Natural
|
workingDays :: [Weekday] -> Day -> Day -> Natural
|
||||||
|
@ -191,7 +191,7 @@ workingDays wds start end = fromIntegral $ daysFull + daysTail
|
||||||
|
|
||||||
-- ASSUME days is a sorted list
|
-- ASSUME days is a sorted list
|
||||||
foldDays
|
foldDays
|
||||||
:: MonadInsertError m
|
:: MonadAppError m
|
||||||
=> (Day -> Day -> m a)
|
=> (Day -> Day -> m a)
|
||||||
-> Day
|
-> Day
|
||||||
-> [Day]
|
-> [Day]
|
||||||
|
@ -201,27 +201,27 @@ foldDays f start days = case NE.nonEmpty days of
|
||||||
Just ds
|
Just ds
|
||||||
| any (start >) ds ->
|
| any (start >) ds ->
|
||||||
throwError $
|
throwError $
|
||||||
InsertException [PeriodError start $ minimum ds]
|
AppException [PeriodError start $ minimum ds]
|
||||||
| otherwise ->
|
| otherwise ->
|
||||||
combineErrors $
|
combineErrors $
|
||||||
snd $
|
snd $
|
||||||
L.mapAccumL (\prevDay day -> (day, f prevDay day)) start days
|
L.mapAccumL (\prevDay day -> (day, f prevDay day)) start days
|
||||||
|
|
||||||
isIncomeAcnt :: (MonadInsertError m, MonadFinance m) => AcntID -> m ()
|
isIncomeAcnt :: (MonadAppError m, MonadFinance m) => AcntID -> m ()
|
||||||
isIncomeAcnt = checkAcntType IncomeT
|
isIncomeAcnt = checkAcntType IncomeT
|
||||||
|
|
||||||
isNotIncomeAcnt :: (MonadInsertError m, MonadFinance m) => AcntID -> m ()
|
isNotIncomeAcnt :: (MonadAppError m, MonadFinance m) => AcntID -> m ()
|
||||||
isNotIncomeAcnt = checkAcntTypes (AssetT :| [EquityT, ExpenseT, LiabilityT])
|
isNotIncomeAcnt = checkAcntTypes (AssetT :| [EquityT, ExpenseT, LiabilityT])
|
||||||
|
|
||||||
checkAcntType
|
checkAcntType
|
||||||
:: (MonadInsertError m, MonadFinance m)
|
:: (MonadAppError m, MonadFinance m)
|
||||||
=> AcntType
|
=> AcntType
|
||||||
-> AcntID
|
-> AcntID
|
||||||
-> m ()
|
-> m ()
|
||||||
checkAcntType t = checkAcntTypes (t :| [])
|
checkAcntType t = checkAcntTypes (t :| [])
|
||||||
|
|
||||||
checkAcntTypes
|
checkAcntTypes
|
||||||
:: (MonadInsertError m, MonadFinance m)
|
:: (MonadAppError m, MonadFinance m)
|
||||||
=> NE.NonEmpty AcntType
|
=> NE.NonEmpty AcntType
|
||||||
-> AcntID
|
-> AcntID
|
||||||
-> m ()
|
-> m ()
|
||||||
|
@ -229,7 +229,7 @@ checkAcntTypes ts i = void $ go =<< lookupAccountType i
|
||||||
where
|
where
|
||||||
go t
|
go t
|
||||||
| t `L.elem` ts = return i
|
| t `L.elem` ts = return i
|
||||||
| otherwise = throwError $ InsertException [AccountError i ts]
|
| otherwise = throwError $ AppException [AccountTypeError i ts]
|
||||||
|
|
||||||
flattenAllo :: SingleAllocation v -> [FlatAllocation v]
|
flattenAllo :: SingleAllocation v -> [FlatAllocation v]
|
||||||
flattenAllo Allocation {alloAmts, alloTo} = fmap go alloAmts
|
flattenAllo Allocation {alloAmts, alloTo} = fmap go alloAmts
|
||||||
|
@ -275,7 +275,7 @@ allocatePre precision gross = L.mapAccumR go M.empty
|
||||||
let v =
|
let v =
|
||||||
if prePercent
|
if prePercent
|
||||||
then gross *. (preValue / 100)
|
then gross *. (preValue / 100)
|
||||||
else realFracToDecimal' precision preValue
|
else realFracToDecimalP precision preValue
|
||||||
in (mapAdd_ preCategory v m, f {faValue = v})
|
in (mapAdd_ preCategory v m, f {faValue = v})
|
||||||
|
|
||||||
allocateTax
|
allocateTax
|
||||||
|
@ -324,14 +324,14 @@ allocatePost prec aftertax = fmap (fmap go)
|
||||||
where
|
where
|
||||||
go PosttaxValue {postValue, postPercent}
|
go PosttaxValue {postValue, postPercent}
|
||||||
| postPercent = aftertax *. (postValue / 100)
|
| postPercent = aftertax *. (postValue / 100)
|
||||||
| otherwise = realFracToDecimal' prec postValue
|
| otherwise = realFracToDecimalP prec postValue
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- shadow transfers
|
-- shadow transfers
|
||||||
|
|
||||||
-- TODO this is going to be O(n*m), which might be a problem?
|
-- TODO this is going to be O(n*m), which might be a problem?
|
||||||
addShadowTransfers
|
addShadowTransfers
|
||||||
:: (MonadInsertError m, MonadFinance m)
|
:: (MonadAppError m, MonadFinance m)
|
||||||
=> [ShadowTransfer]
|
=> [ShadowTransfer]
|
||||||
-> [Tx CommitR]
|
-> [Tx CommitR]
|
||||||
-> m [Tx CommitR]
|
-> m [Tx CommitR]
|
||||||
|
@ -342,7 +342,7 @@ addShadowTransfers ms = mapErrors go
|
||||||
return $ tx {txOther = Right <$> es}
|
return $ tx {txOther = Right <$> es}
|
||||||
|
|
||||||
fromShadow
|
fromShadow
|
||||||
:: (MonadInsertError m, MonadFinance m)
|
:: (MonadAppError m, MonadFinance m)
|
||||||
=> Tx CommitR
|
=> Tx CommitR
|
||||||
-> ShadowTransfer
|
-> ShadowTransfer
|
||||||
-> m (Maybe ShadowEntrySet)
|
-> m (Maybe ShadowEntrySet)
|
||||||
|
@ -354,7 +354,7 @@ fromShadow tx ShadowTransfer {stFrom, stTo, stDesc, stRatio, stCurrency, stMatch
|
||||||
curRes = lookupCurrencyKey (CurID stCurrency)
|
curRes = lookupCurrencyKey (CurID stCurrency)
|
||||||
shaRes = liftExcept $ shadowMatches stMatch tx
|
shaRes = liftExcept $ shadowMatches stMatch tx
|
||||||
|
|
||||||
shadowMatches :: TransferMatcher -> Tx CommitR -> InsertExcept Bool
|
shadowMatches :: TransferMatcher -> Tx CommitR -> AppExcept Bool
|
||||||
shadowMatches TransferMatcher {tmFrom, tmTo, tmDate, tmVal} Tx {txPrimary, txDate} = do
|
shadowMatches TransferMatcher {tmFrom, tmTo, tmDate, tmVal} Tx {txPrimary, txDate} = do
|
||||||
-- NOTE this will only match against the primary entry set since those
|
-- NOTE this will only match against the primary entry set since those
|
||||||
-- are what are guaranteed to exist from a transfer
|
-- are what are guaranteed to exist from a transfer
|
||||||
|
|
|
@ -107,7 +107,7 @@ nukeTables = do
|
||||||
-- toBal = maybe "???" (fmtRational 2) . unValue
|
-- toBal = maybe "???" (fmtRational 2) . unValue
|
||||||
|
|
||||||
readConfigState
|
readConfigState
|
||||||
:: (MonadInsertError m, MonadSqlQuery m)
|
:: (MonadAppError m, MonadSqlQuery m)
|
||||||
=> Config
|
=> Config
|
||||||
-> [Budget]
|
-> [Budget]
|
||||||
-> [History]
|
-> [History]
|
||||||
|
@ -160,22 +160,23 @@ readConfigState c bs hs = do
|
||||||
resolveScope f = liftExcept $ resolveDaySpan $ f $ scope c
|
resolveScope f = liftExcept $ resolveDaySpan $ f $ scope c
|
||||||
|
|
||||||
readScopeChanged
|
readScopeChanged
|
||||||
:: (MonadInsertError m, MonadSqlQuery m)
|
:: (MonadAppError m, MonadSqlQuery m)
|
||||||
=> Bool
|
=> Bool
|
||||||
-> BudgetSpan
|
-> BudgetSpan
|
||||||
-> HistorySpan
|
-> HistorySpan
|
||||||
-> m (Bool, Bool)
|
-> m (Bool, Bool)
|
||||||
readScopeChanged dbempty bscope hscope = do
|
readScopeChanged dbempty bscope hscope = do
|
||||||
rs <- dumpTbl
|
rs <- dumpTbl
|
||||||
|
-- TODO these errors should only fire when someone messed with the DB
|
||||||
case rs of
|
case rs of
|
||||||
[] -> if dbempty then return (True, True) else throwError undefined
|
[] -> if dbempty then return (True, True) else throwAppError $ DBError DBShouldBeEmpty
|
||||||
[r] -> do
|
[r] -> do
|
||||||
let (ConfigStateR h b) = E.entityVal r
|
let (ConfigStateR h b) = E.entityVal r
|
||||||
return (bscope /= b, hscope /= h)
|
return (bscope /= b, hscope /= h)
|
||||||
_ -> throwError undefined
|
_ -> throwAppError $ DBError DBMultiScope
|
||||||
|
|
||||||
makeTxCRUD
|
makeTxCRUD
|
||||||
:: (MonadInsertError m, MonadSqlQuery m, Hashable a)
|
:: (MonadAppError m, MonadSqlQuery m, Hashable a)
|
||||||
=> ExistingConfig
|
=> ExistingConfig
|
||||||
-> [a]
|
-> [a]
|
||||||
-> [CommitHash]
|
-> [CommitHash]
|
||||||
|
@ -354,9 +355,10 @@ trimNames = fmap (AcntID . T.intercalate "_") . go []
|
||||||
(_ :| []) -> [key : prev]
|
(_ :| []) -> [key : prev]
|
||||||
([] :| xs) ->
|
([] :| xs) ->
|
||||||
let next = key : prev
|
let next = key : prev
|
||||||
other = go next $ fmap (fromMaybe undefined . NE.nonEmpty) xs
|
other = go next $ fmap (fromMaybe err . NE.nonEmpty) xs
|
||||||
in next : other
|
in next : other
|
||||||
(x :| xs) -> go (key : prev) $ fmap (fromMaybe undefined . NE.nonEmpty) (x : xs)
|
(x :| xs) -> go (key : prev) $ fmap (fromMaybe err . NE.nonEmpty) (x : xs)
|
||||||
|
err = error "account path list either not sorted or contains duplicates"
|
||||||
|
|
||||||
groupNonEmpty :: Ord a => [NonEmpty a] -> [(a, NonEmpty [a])]
|
groupNonEmpty :: Ord a => [NonEmpty a] -> [(a, NonEmpty [a])]
|
||||||
groupNonEmpty = fmap (second (NE.tail <$>)) . groupWith NE.head
|
groupNonEmpty = fmap (second (NE.tail <$>)) . groupWith NE.head
|
||||||
|
@ -453,7 +455,7 @@ readInvalidIds ExistingConfig {ecAccounts, ecCurrencies, ecTags} xs = do
|
||||||
. groupKey id
|
. groupKey id
|
||||||
|
|
||||||
readUpdates
|
readUpdates
|
||||||
:: (MonadInsertError m, MonadSqlQuery m)
|
:: (MonadAppError m, MonadSqlQuery m)
|
||||||
=> [CommitHash]
|
=> [CommitHash]
|
||||||
-> m ([ReadEntry], [Either TotalUpdateEntrySet FullUpdateEntrySet])
|
-> m ([ReadEntry], [Either TotalUpdateEntrySet FullUpdateEntrySet])
|
||||||
readUpdates hashes = do
|
readUpdates hashes = do
|
||||||
|
@ -517,7 +519,7 @@ readUpdates hashes = do
|
||||||
, utToRO = toRO
|
, utToRO = toRO
|
||||||
, utFromUnk = fromUnk
|
, utFromUnk = fromUnk
|
||||||
, utToUnk = toUnk
|
, utToUnk = toUnk
|
||||||
, utTotalValue = realFracToDecimal' prec' tot
|
, utTotalValue = realFracToDecimalP prec' tot
|
||||||
, utBudget = E.unValue name
|
, utBudget = E.unValue name
|
||||||
, utPriority = E.unValue pri
|
, utPriority = E.unValue pri
|
||||||
}
|
}
|
||||||
|
@ -536,7 +538,8 @@ readUpdates hashes = do
|
||||||
, utBudget = E.unValue name
|
, utBudget = E.unValue name
|
||||||
, utPriority = E.unValue pri
|
, utPriority = E.unValue pri
|
||||||
}
|
}
|
||||||
_ -> throwError undefined
|
-- TODO this error is lame
|
||||||
|
_ -> throwAppError $ DBError $ DBUpdateUnbalanced
|
||||||
makeRE ((_, day, name, pri, (curID, prec)), entry) = do
|
makeRE ((_, day, name, pri, (curID, prec)), entry) = do
|
||||||
let e = entityVal entry
|
let e = entityVal entry
|
||||||
in ReadEntry
|
in ReadEntry
|
||||||
|
@ -551,7 +554,7 @@ readUpdates hashes = do
|
||||||
splitFrom
|
splitFrom
|
||||||
:: Precision
|
:: Precision
|
||||||
-> NonEmpty (EntryRId, EntryR)
|
-> NonEmpty (EntryRId, EntryR)
|
||||||
-> InsertExcept (Either UEBlank (Either UE_RO UEUnk), [UE_RO], [UEUnk])
|
-> AppExcept (Either UEBlank (Either UE_RO UEUnk), [UE_RO], [UEUnk])
|
||||||
splitFrom prec (f0 :| fs) = do
|
splitFrom prec (f0 :| fs) = do
|
||||||
-- ASSUME entries are sorted by index
|
-- ASSUME entries are sorted by index
|
||||||
-- TODO combine errors here
|
-- TODO combine errors here
|
||||||
|
@ -567,7 +570,7 @@ splitTo
|
||||||
-> Either UEBlank (Either UE_RO UEUnk)
|
-> Either UEBlank (Either UE_RO UEUnk)
|
||||||
-> [UEUnk]
|
-> [UEUnk]
|
||||||
-> NonEmpty (EntryRId, EntryR)
|
-> NonEmpty (EntryRId, EntryR)
|
||||||
-> InsertExcept
|
-> AppExcept
|
||||||
( Either (UEBlank, [UELink]) (Either UE_RO (UEUnk, [UELink]))
|
( Either (UEBlank, [UELink]) (Either UE_RO (UEUnk, [UELink]))
|
||||||
, [(UEUnk, [UELink])]
|
, [(UEUnk, [UELink])]
|
||||||
, UEBlank
|
, UEBlank
|
||||||
|
@ -621,7 +624,7 @@ zipPaired
|
||||||
:: Precision
|
:: Precision
|
||||||
-> [UEUnk]
|
-> [UEUnk]
|
||||||
-> [(EntryIndex, NonEmpty (EntryRId, EntryR))]
|
-> [(EntryIndex, NonEmpty (EntryRId, EntryR))]
|
||||||
-> InsertExcept ([(UEUnk, [UELink])], [UE_RO])
|
-> AppExcept ([(UEUnk, [UELink])], [UE_RO])
|
||||||
zipPaired prec = go ([], [])
|
zipPaired prec = go ([], [])
|
||||||
where
|
where
|
||||||
nolinks = ((,[]) <$>)
|
nolinks = ((,[]) <$>)
|
||||||
|
@ -639,41 +642,46 @@ zipPaired prec = go ([], [])
|
||||||
let f = maybe (second (++ ros)) (\u -> first (u :)) nextLink
|
let f = maybe (second (++ ros)) (\u -> first (u :)) nextLink
|
||||||
go (f acc') fs' ts
|
go (f acc') fs' ts
|
||||||
|
|
||||||
makeLinkUnk :: (EntryRId, EntryR) -> InsertExcept UELink
|
makeLinkUnk :: (EntryRId, EntryR) -> AppExcept UELink
|
||||||
makeLinkUnk (k, e) =
|
makeLinkUnk (k, e) =
|
||||||
|
-- TODO error should state that scale must be present for a link in the db
|
||||||
maybe
|
maybe
|
||||||
(throwError $ InsertException undefined)
|
(throwAppError $ DBError $ DBLinkError k DBLinkNoScale)
|
||||||
(return . makeUE k e . LinkScale)
|
(return . makeUE k e . LinkScale)
|
||||||
$ fromRational <$> entryRCachedValue e
|
$ fromRational <$> entryRCachedValue e
|
||||||
|
|
||||||
splitDeferredValue :: Precision -> (EntryRId, EntryR) -> InsertExcept (Either UE_RO UEUnk)
|
splitDeferredValue :: Precision -> (EntryRId, EntryR) -> AppExcept (Either UE_RO UEUnk)
|
||||||
splitDeferredValue prec p = do
|
splitDeferredValue prec p@(k, _) = do
|
||||||
res <- readDeferredValue prec p
|
res <- readDeferredValue prec p
|
||||||
case res of
|
case res of
|
||||||
Left _ -> throwError $ InsertException undefined
|
Left _ -> throwAppError $ DBError $ DBLinkError k DBLinkNoValue
|
||||||
Right x -> return x
|
Right x -> return x
|
||||||
|
|
||||||
readDeferredValue :: Precision -> (EntryRId, EntryR) -> InsertExcept (Either UEBlank (Either UE_RO UEUnk))
|
readDeferredValue :: Precision -> (EntryRId, EntryR) -> AppExcept (Either UEBlank (Either UE_RO UEUnk))
|
||||||
readDeferredValue prec (k, e) = case (entryRCachedValue e, entryRCachedType e) of
|
readDeferredValue prec (k, e) = case (entryRCachedValue e, entryRCachedType e) of
|
||||||
(Nothing, Just TFixed) -> return $ Right $ Left $ makeRoUE prec e
|
(Nothing, Just TFixed) -> return $ Right $ Left $ makeRoUE prec e
|
||||||
(Just v, Just TBalance) -> go $ fmap EVBalance $ makeUE k e $ realFracToDecimal' prec v
|
(Just v, Just TBalance) -> go $ fmap EVBalance $ makeUE k e $ realFracToDecimalP prec v
|
||||||
(Just v, Just TPercent) -> go $ fmap EVPercent $ makeUE k e $ fromRational v
|
(Just v, Just TPercent) -> go $ fmap EVPercent $ makeUE k e $ fromRational v
|
||||||
(Nothing, Nothing) -> return $ Left $ makeUnkUE k e
|
(Nothing, Nothing) -> return $ Left $ makeUnkUE k e
|
||||||
_ -> throwError $ InsertException undefined
|
(Just v, Nothing) -> err $ DBLinkInvalidValue v False
|
||||||
|
(Just v, Just TFixed) -> err $ DBLinkInvalidValue v True
|
||||||
|
(Nothing, Just TBalance) -> err $ DBLinkInvalidBalance
|
||||||
|
(Nothing, Just TPercent) -> err $ DBLinkInvalidPercent
|
||||||
where
|
where
|
||||||
go = return . Right . Right
|
go = return . Right . Right
|
||||||
|
err = throwAppError . DBError . DBLinkError k
|
||||||
|
|
||||||
makeUE :: i -> EntryR -> v -> UpdateEntry i v
|
makeUE :: i -> EntryR -> v -> UpdateEntry i v
|
||||||
makeUE k e v = UpdateEntry k (entryRAccount e) v (entryRIndex e)
|
makeUE k e v = UpdateEntry k (entryRAccount e) v (entryRIndex e)
|
||||||
|
|
||||||
makeRoUE :: Precision -> EntryR -> UpdateEntry () StaticValue
|
makeRoUE :: Precision -> EntryR -> UpdateEntry () StaticValue
|
||||||
makeRoUE prec e = makeUE () e $ StaticValue (realFracToDecimal' prec $ entryRValue e)
|
makeRoUE prec e = makeUE () e $ StaticValue (realFracToDecimalP prec $ entryRValue e)
|
||||||
|
|
||||||
makeUnkUE :: EntryRId -> EntryR -> UpdateEntry EntryRId ()
|
makeUnkUE :: EntryRId -> EntryR -> UpdateEntry EntryRId ()
|
||||||
makeUnkUE k e = makeUE k e ()
|
makeUnkUE k e = makeUE k e ()
|
||||||
|
|
||||||
insertAll
|
insertAll
|
||||||
:: (MonadInsertError m, MonadSqlQuery m, MonadFinance m)
|
:: (MonadAppError m, MonadSqlQuery m, MonadFinance m)
|
||||||
=> [EntryCRU]
|
=> [EntryCRU]
|
||||||
-> m ()
|
-> m ()
|
||||||
insertAll ebs = do
|
insertAll ebs = do
|
||||||
|
|
|
@ -37,7 +37,7 @@ splitHistory = partitionEithers . fmap go
|
||||||
-- Transfers
|
-- Transfers
|
||||||
|
|
||||||
readHistTransfer
|
readHistTransfer
|
||||||
:: (MonadInsertError m, MonadFinance m)
|
:: (MonadAppError m, MonadFinance m)
|
||||||
=> PairedTransfer
|
=> PairedTransfer
|
||||||
-> m [Tx CommitR]
|
-> m [Tx CommitR]
|
||||||
readHistTransfer ht = do
|
readHistTransfer ht = do
|
||||||
|
@ -82,9 +82,9 @@ readImport_
|
||||||
-> m [TxRecord]
|
-> m [TxRecord]
|
||||||
readImport_ n delim tns p = do
|
readImport_ n delim tns p = do
|
||||||
res <- tryIO $ BL.readFile p
|
res <- tryIO $ BL.readFile p
|
||||||
bs <- fromEither $ first (InsertException . (: []) . InsertIOError . tshow) res
|
bs <- fromEither $ first (AppException . (: []) . StatementIOError . tshow) res
|
||||||
case decodeByNameWithP (parseTxRecord p tns) opts $ skip bs of
|
case decodeByNameWithP (parseTxRecord p tns) opts $ skip bs of
|
||||||
Left m -> throwIO $ InsertException [ParseError $ T.pack m]
|
Left m -> throwIO $ AppException [ParseError $ T.pack m]
|
||||||
Right (_, v) -> return $ catMaybes $ V.toList v
|
Right (_, v) -> return $ catMaybes $ V.toList v
|
||||||
where
|
where
|
||||||
opts = defaultDecodeOptions {decDelimiter = fromIntegral delim}
|
opts = defaultDecodeOptions {decDelimiter = fromIntegral delim}
|
||||||
|
@ -104,12 +104,12 @@ parseTxRecord p TxOpts {toDate, toAmountFmt, toDesc, toAmount, toOther, toDateFm
|
||||||
d' <- parseTimeM True defaultTimeLocale (T.unpack toDateFmt) d
|
d' <- parseTimeM True defaultTimeLocale (T.unpack toDateFmt) d
|
||||||
return $ Just $ TxRecord d' a e os p
|
return $ Just $ TxRecord d' a e os p
|
||||||
|
|
||||||
matchRecords :: MonadFinance m => [MatchRe] -> [TxRecord] -> InsertExceptT m [Tx ()]
|
matchRecords :: MonadFinance m => [MatchRe] -> [TxRecord] -> AppExceptT m [Tx ()]
|
||||||
matchRecords ms rs = do
|
matchRecords ms rs = do
|
||||||
(matched, unmatched, notfound) <- matchAll (matchPriorities ms) rs
|
(matched, unmatched, notfound) <- matchAll (matchPriorities ms) rs
|
||||||
case (matched, unmatched, notfound) of
|
case (matched, unmatched, notfound) of
|
||||||
(ms_, [], []) -> return ms_
|
(ms_, [], []) -> return ms_
|
||||||
(_, us, ns) -> throwError $ InsertException [StatementError us ns]
|
(_, us, ns) -> throwError $ AppException [StatementError us ns]
|
||||||
|
|
||||||
matchPriorities :: [MatchRe] -> [MatchGroup]
|
matchPriorities :: [MatchRe] -> [MatchGroup]
|
||||||
matchPriorities =
|
matchPriorities =
|
||||||
|
@ -166,7 +166,7 @@ zipperMatch
|
||||||
:: MonadFinance m
|
:: MonadFinance m
|
||||||
=> Unzipped MatchRe
|
=> Unzipped MatchRe
|
||||||
-> TxRecord
|
-> TxRecord
|
||||||
-> InsertExceptT m (Zipped MatchRe, MatchRes (Tx ()))
|
-> AppExceptT m (Zipped MatchRe, MatchRes (Tx ()))
|
||||||
zipperMatch (Unzipped bs cs as) x = go [] cs
|
zipperMatch (Unzipped bs cs as) x = go [] cs
|
||||||
where
|
where
|
||||||
go _ [] = return (Zipped bs $ cs ++ as, MatchFail)
|
go _ [] = return (Zipped bs $ cs ++ as, MatchFail)
|
||||||
|
@ -183,7 +183,7 @@ zipperMatch'
|
||||||
:: MonadFinance m
|
:: MonadFinance m
|
||||||
=> Zipped MatchRe
|
=> Zipped MatchRe
|
||||||
-> TxRecord
|
-> TxRecord
|
||||||
-> InsertExceptT m (Zipped MatchRe, MatchRes (Tx ()))
|
-> AppExceptT m (Zipped MatchRe, MatchRes (Tx ()))
|
||||||
zipperMatch' z x = go z
|
zipperMatch' z x = go z
|
||||||
where
|
where
|
||||||
go (Zipped bs (a : as)) = do
|
go (Zipped bs (a : as)) = do
|
||||||
|
@ -204,7 +204,7 @@ matchAll
|
||||||
:: MonadFinance m
|
:: MonadFinance m
|
||||||
=> [MatchGroup]
|
=> [MatchGroup]
|
||||||
-> [TxRecord]
|
-> [TxRecord]
|
||||||
-> InsertExceptT m ([Tx ()], [TxRecord], [MatchRe])
|
-> AppExceptT m ([Tx ()], [TxRecord], [MatchRe])
|
||||||
matchAll = go ([], [])
|
matchAll = go ([], [])
|
||||||
where
|
where
|
||||||
go (matched, unused) gs rs = case (gs, rs) of
|
go (matched, unused) gs rs = case (gs, rs) of
|
||||||
|
@ -218,7 +218,7 @@ matchGroup
|
||||||
:: MonadFinance m
|
:: MonadFinance m
|
||||||
=> MatchGroup
|
=> MatchGroup
|
||||||
-> [TxRecord]
|
-> [TxRecord]
|
||||||
-> InsertExceptT m ([Tx ()], [TxRecord], [MatchRe])
|
-> AppExceptT m ([Tx ()], [TxRecord], [MatchRe])
|
||||||
matchGroup MatchGroup {mgDate = ds, mgNoDate = ns} rs = do
|
matchGroup MatchGroup {mgDate = ds, mgNoDate = ns} rs = do
|
||||||
(md, rest, ud) <- matchDates ds rs
|
(md, rest, ud) <- matchDates ds rs
|
||||||
(mn, unmatched, un) <- matchNonDates ns rest
|
(mn, unmatched, un) <- matchNonDates ns rest
|
||||||
|
@ -228,7 +228,7 @@ matchDates
|
||||||
:: MonadFinance m
|
:: MonadFinance m
|
||||||
=> [MatchRe]
|
=> [MatchRe]
|
||||||
-> [TxRecord]
|
-> [TxRecord]
|
||||||
-> InsertExceptT m ([Tx ()], [TxRecord], [MatchRe])
|
-> AppExceptT m ([Tx ()], [TxRecord], [MatchRe])
|
||||||
matchDates ms = go ([], [], initZipper ms)
|
matchDates ms = go ([], [], initZipper ms)
|
||||||
where
|
where
|
||||||
go (matched, unmatched, z) [] =
|
go (matched, unmatched, z) [] =
|
||||||
|
@ -253,7 +253,7 @@ matchNonDates
|
||||||
:: MonadFinance m
|
:: MonadFinance m
|
||||||
=> [MatchRe]
|
=> [MatchRe]
|
||||||
-> [TxRecord]
|
-> [TxRecord]
|
||||||
-> InsertExceptT m ([Tx ()], [TxRecord], [MatchRe])
|
-> AppExceptT m ([Tx ()], [TxRecord], [MatchRe])
|
||||||
matchNonDates ms = go ([], [], initZipper ms)
|
matchNonDates ms = go ([], [], initZipper ms)
|
||||||
where
|
where
|
||||||
go (matched, unmatched, z) [] =
|
go (matched, unmatched, z) [] =
|
||||||
|
@ -270,7 +270,7 @@ matchNonDates ms = go ([], [], initZipper ms)
|
||||||
MatchFail -> (matched, r : unmatched)
|
MatchFail -> (matched, r : unmatched)
|
||||||
in go (m, u, resetZipper z') rs
|
in go (m, u, resetZipper z') rs
|
||||||
|
|
||||||
matches :: MonadFinance m => MatchRe -> TxRecord -> InsertExceptT m (MatchRes (Tx ()))
|
matches :: MonadFinance m => MatchRe -> TxRecord -> AppExceptT m (MatchRes (Tx ()))
|
||||||
matches
|
matches
|
||||||
StatementParser {spTx, spOther, spVal, spDate, spDesc, spPriority}
|
StatementParser {spTx, spOther, spVal, spDate, spDesc, spPriority}
|
||||||
r@TxRecord {trDate, trAmount, trDesc, trOther} = do
|
r@TxRecord {trDate, trAmount, trDesc, trOther} = do
|
||||||
|
@ -287,7 +287,7 @@ matches
|
||||||
desc = maybe (return True) (matchMaybe (unTxDesc trDesc) . snd) spDesc
|
desc = maybe (return True) (matchMaybe (unTxDesc trDesc) . snd) spDesc
|
||||||
convert tg = MatchPass <$> toTx (fromIntegral spPriority) tg r
|
convert tg = MatchPass <$> toTx (fromIntegral spPriority) tg r
|
||||||
|
|
||||||
toTx :: MonadFinance m => Int -> TxGetter -> TxRecord -> InsertExceptT m (Tx ())
|
toTx :: MonadFinance m => Int -> TxGetter -> TxRecord -> AppExceptT m (Tx ())
|
||||||
toTx
|
toTx
|
||||||
priority
|
priority
|
||||||
TxGetter
|
TxGetter
|
||||||
|
@ -329,7 +329,7 @@ resolveSubGetter
|
||||||
:: MonadFinance m
|
:: MonadFinance m
|
||||||
=> TxRecord
|
=> TxRecord
|
||||||
-> TxSubGetter
|
-> TxSubGetter
|
||||||
-> InsertExceptT m SecondayEntrySet
|
-> AppExceptT m SecondayEntrySet
|
||||||
resolveSubGetter r TxSubGetter {tsgFrom, tsgTo, tsgValue, tsgCurrency} = do
|
resolveSubGetter r TxSubGetter {tsgFrom, tsgTo, tsgValue, tsgCurrency} = do
|
||||||
m <- asks csCurrencyMap
|
m <- asks csCurrencyMap
|
||||||
cur <- liftInner $ resolveCurrency m r tsgCurrency
|
cur <- liftInner $ resolveCurrency m r tsgCurrency
|
||||||
|
@ -347,12 +347,12 @@ resolveSubGetter r TxSubGetter {tsgFrom, tsgTo, tsgValue, tsgCurrency} = do
|
||||||
}
|
}
|
||||||
|
|
||||||
resolveHalfEntry
|
resolveHalfEntry
|
||||||
:: (Precision -> TxRecord -> n -> InsertExcept v')
|
:: (Precision -> TxRecord -> n -> AppExcept v')
|
||||||
-> Precision
|
-> Precision
|
||||||
-> TxRecord
|
-> TxRecord
|
||||||
-> v
|
-> v
|
||||||
-> TxHalfGetter (EntryGetter n)
|
-> TxHalfGetter (EntryGetter n)
|
||||||
-> InsertExcept (HalfEntrySet v v')
|
-> AppExcept (HalfEntrySet v v')
|
||||||
resolveHalfEntry f prec r v TxHalfGetter {thgAcnt, thgComment, thgTags, thgEntries} =
|
resolveHalfEntry f prec r v TxHalfGetter {thgAcnt, thgComment, thgTags, thgEntries} =
|
||||||
combineError acntRes esRes $ \a es ->
|
combineError acntRes esRes $ \a es ->
|
||||||
HalfEntrySet
|
HalfEntrySet
|
||||||
|
@ -369,7 +369,7 @@ resolveHalfEntry f prec r v TxHalfGetter {thgAcnt, thgComment, thgTags, thgEntri
|
||||||
acntRes = resolveAcnt r thgAcnt
|
acntRes = resolveAcnt r thgAcnt
|
||||||
esRes = mapErrors (resolveEntry f prec r) thgEntries
|
esRes = mapErrors (resolveEntry f prec r) thgEntries
|
||||||
|
|
||||||
otherMatches :: M.Map T.Text T.Text -> FieldMatcherRe -> InsertExcept Bool
|
otherMatches :: M.Map T.Text T.Text -> FieldMatcherRe -> AppExcept Bool
|
||||||
otherMatches dict m = case m of
|
otherMatches dict m = case m of
|
||||||
Val (Field n mv) -> valMatches mv =<< (readRational =<< lookup_ MatchNumeric n)
|
Val (Field n mv) -> valMatches mv =<< (readRational =<< lookup_ MatchNumeric n)
|
||||||
Desc (Field n (_, md)) -> (`matchMaybe` md) =<< lookup_ MatchText n
|
Desc (Field n (_, md)) -> (`matchMaybe` md) =<< lookup_ MatchText n
|
||||||
|
@ -377,25 +377,25 @@ otherMatches dict m = case m of
|
||||||
lookup_ t n = lookupErr (MatchField t) n dict
|
lookup_ t n = lookupErr (MatchField t) n dict
|
||||||
|
|
||||||
resolveEntry
|
resolveEntry
|
||||||
:: (Precision -> TxRecord -> n -> InsertExcept v)
|
:: (Precision -> TxRecord -> n -> AppExcept v)
|
||||||
-> Precision
|
-> Precision
|
||||||
-> TxRecord
|
-> TxRecord
|
||||||
-> EntryGetter n
|
-> EntryGetter n
|
||||||
-> InsertExcept (Entry AcntID v TagID)
|
-> AppExcept (Entry AcntID v TagID)
|
||||||
resolveEntry f prec r s@Entry {eAcnt, eValue} =
|
resolveEntry f prec r s@Entry {eAcnt, eValue} =
|
||||||
combineError acntRes valRes $ \a v -> s {eAcnt = a, eValue = v}
|
combineError acntRes valRes $ \a v -> s {eAcnt = a, eValue = v}
|
||||||
where
|
where
|
||||||
acntRes = resolveAcnt r eAcnt
|
acntRes = resolveAcnt r eAcnt
|
||||||
valRes = f prec r eValue
|
valRes = f prec r eValue
|
||||||
|
|
||||||
resolveFromValue :: Precision -> TxRecord -> EntryNumGetter -> InsertExcept EntryValue
|
resolveFromValue :: Precision -> TxRecord -> EntryNumGetter -> AppExcept EntryValue
|
||||||
resolveFromValue = resolveValue
|
resolveFromValue = resolveValue
|
||||||
|
|
||||||
resolveToValue :: Precision -> TxRecord -> LinkedEntryNumGetter -> InsertExcept EntryLink
|
resolveToValue :: Precision -> TxRecord -> LinkedEntryNumGetter -> AppExcept EntryLink
|
||||||
resolveToValue _ _ (Linked l) = return $ LinkIndex l
|
resolveToValue _ _ (Linked l) = return $ LinkIndex l
|
||||||
resolveToValue prec r (Getter g) = LinkValue <$> resolveValue prec r g
|
resolveToValue prec r (Getter g) = LinkValue <$> resolveValue prec r g
|
||||||
|
|
||||||
resolveValue :: Precision -> TxRecord -> EntryNumGetter -> InsertExcept EntryValue
|
resolveValue :: Precision -> TxRecord -> EntryNumGetter -> AppExcept EntryValue
|
||||||
resolveValue prec TxRecord {trOther, trAmount} s = case s of
|
resolveValue prec TxRecord {trOther, trAmount} s = case s of
|
||||||
(LookupN t) -> EntryFixed . go <$> (readDouble =<< lookupErr EntryValField t trOther)
|
(LookupN t) -> EntryFixed . go <$> (readDouble =<< lookupErr EntryValField t trOther)
|
||||||
(ConstN c) -> return $ EntryFixed $ go c
|
(ConstN c) -> return $ EntryFixed $ go c
|
||||||
|
@ -403,20 +403,19 @@ resolveValue prec TxRecord {trOther, trAmount} s = case s of
|
||||||
BalanceN x -> return $ EntryBalance $ go x
|
BalanceN x -> return $ EntryBalance $ go x
|
||||||
PercentN x -> return $ EntryPercent x
|
PercentN x -> return $ EntryPercent x
|
||||||
where
|
where
|
||||||
go = realFracToDecimal' prec
|
go = realFracToDecimalP prec
|
||||||
|
|
||||||
resolveAcnt :: TxRecord -> EntryAcnt -> InsertExcept AcntID
|
resolveAcnt :: TxRecord -> EntryAcnt -> AppExcept AcntID
|
||||||
resolveAcnt r e = AcntID <$> resolveEntryField AcntField r (unAcntID <$> e)
|
resolveAcnt r e = AcntID <$> resolveEntryField AcntField r (unAcntID <$> e)
|
||||||
|
|
||||||
resolveCurrency :: CurrencyMap -> TxRecord -> EntryCur -> InsertExcept CurrencyPrec
|
resolveCurrency :: CurrencyMap -> TxRecord -> EntryCur -> AppExcept CurrencyPrec
|
||||||
resolveCurrency m r c = do
|
resolveCurrency m r c = do
|
||||||
i <- resolveEntryField CurField r (unCurID <$> c)
|
i <- resolveEntryField CurField r (unCurID <$> c)
|
||||||
case M.lookup (CurID i) m of
|
case M.lookup (CurID i) m of
|
||||||
Just k -> return k
|
Just k -> return k
|
||||||
-- TODO this should be its own error (I think)
|
Nothing -> throwError $ AppException [LookupError (DBKey CurField) i]
|
||||||
Nothing -> throwError $ InsertException [LookupError (EntryIDField CurField) undefined]
|
|
||||||
|
|
||||||
resolveEntryField :: EntryIDType -> TxRecord -> EntryTextGetter T.Text -> InsertExcept T.Text
|
resolveEntryField :: EntryIDType -> TxRecord -> EntryTextGetter T.Text -> AppExcept T.Text
|
||||||
resolveEntryField t TxRecord {trOther = o} s = case s of
|
resolveEntryField t TxRecord {trOther = o} s = case s of
|
||||||
ConstT p -> return p
|
ConstT p -> return p
|
||||||
LookupT f -> lookup_ f o
|
LookupT f -> lookup_ f o
|
||||||
|
@ -427,15 +426,15 @@ resolveEntryField t TxRecord {trOther = o} s = case s of
|
||||||
(k1, k2) <- combineError (lookup_ f1 o) (lookup_ f2 o) (,)
|
(k1, k2) <- combineError (lookup_ f1 o) (lookup_ f2 o) (,)
|
||||||
lookup_ (k1, k2) m
|
lookup_ (k1, k2) m
|
||||||
where
|
where
|
||||||
lookup_ :: (Ord k, Show k) => k -> M.Map k v -> InsertExcept v
|
lookup_ :: (Ord k, Show k) => k -> M.Map k v -> AppExcept v
|
||||||
lookup_ = lookupErr (EntryIDField t)
|
lookup_ = lookupErr (EntryIDField t)
|
||||||
|
|
||||||
readDouble :: T.Text -> InsertExcept Double
|
readDouble :: T.Text -> AppExcept Double
|
||||||
readDouble s = case readMaybe $ T.unpack s of
|
readDouble s = case readMaybe $ T.unpack s of
|
||||||
Just x -> return x
|
Just x -> return x
|
||||||
Nothing -> throwError $ InsertException [ConversionError s]
|
Nothing -> throwError $ AppException [ConversionError s True]
|
||||||
|
|
||||||
readRational :: T.Text -> InsertExcept Rational
|
readRational :: T.Text -> AppExcept Rational
|
||||||
readRational s = case T.split (== '.') s of
|
readRational s = case T.split (== '.') s of
|
||||||
[x] -> maybe err (return . fromInteger) $ readT x
|
[x] -> maybe err (return . fromInteger) $ readT x
|
||||||
[x, y] -> case (readT x, readT y) of
|
[x, y] -> case (readT x, readT y) of
|
||||||
|
@ -447,14 +446,14 @@ readRational s = case T.split (== '.') s of
|
||||||
_ -> err
|
_ -> err
|
||||||
where
|
where
|
||||||
readT = readMaybe . T.unpack
|
readT = readMaybe . T.unpack
|
||||||
err = throwError $ InsertException [ConversionError s]
|
err = throwError $ AppException [ConversionError s False]
|
||||||
|
|
||||||
compileOptions :: TxOpts T.Text -> InsertExcept TxOptsRe
|
compileOptions :: TxOpts T.Text -> AppExcept TxOptsRe
|
||||||
compileOptions o@TxOpts {toAmountFmt = pat} = do
|
compileOptions o@TxOpts {toAmountFmt = pat} = do
|
||||||
re <- compileRegex True pat
|
re <- compileRegex True pat
|
||||||
return $ o {toAmountFmt = re}
|
return $ o {toAmountFmt = re}
|
||||||
|
|
||||||
compileMatch :: StatementParser T.Text -> InsertExcept MatchRe
|
compileMatch :: StatementParser T.Text -> AppExcept MatchRe
|
||||||
compileMatch m@StatementParser {spDesc, spOther} = do
|
compileMatch m@StatementParser {spDesc, spOther} = do
|
||||||
combineError dres ores $ \d os -> m {spDesc = d, spOther = os}
|
combineError dres ores $ \d os -> m {spDesc = d, spOther = os}
|
||||||
where
|
where
|
||||||
|
@ -462,10 +461,10 @@ compileMatch m@StatementParser {spDesc, spOther} = do
|
||||||
dres = mapM go spDesc
|
dres = mapM go spDesc
|
||||||
ores = combineErrors $ fmap (mapM go) spOther
|
ores = combineErrors $ fmap (mapM go) spOther
|
||||||
|
|
||||||
compileRegex :: Bool -> T.Text -> InsertExcept (Text, Regex)
|
compileRegex :: Bool -> T.Text -> AppExcept (Text, Regex)
|
||||||
compileRegex groups pat = case res of
|
compileRegex groups pat = case res of
|
||||||
Right re -> return (pat, re)
|
Right re -> return (pat, re)
|
||||||
Left _ -> throwError $ InsertException [RegexError pat]
|
Left _ -> throwError $ AppException [RegexError pat]
|
||||||
where
|
where
|
||||||
res =
|
res =
|
||||||
compile
|
compile
|
||||||
|
@ -473,10 +472,10 @@ compileRegex groups pat = case res of
|
||||||
(blankExecOpt {captureGroups = groups})
|
(blankExecOpt {captureGroups = groups})
|
||||||
pat
|
pat
|
||||||
|
|
||||||
matchMaybe :: T.Text -> Regex -> InsertExcept Bool
|
matchMaybe :: T.Text -> Regex -> AppExcept Bool
|
||||||
matchMaybe q re = case execute re q of
|
matchMaybe q re = case execute re q of
|
||||||
Right res -> return $ isJust res
|
Right res -> return $ isJust res
|
||||||
Left _ -> throwError $ InsertException [RegexError "this should not happen"]
|
Left _ -> throwError $ AppException [RegexError "this should not happen"]
|
||||||
|
|
||||||
matchGroupsMaybe :: T.Text -> Regex -> [T.Text]
|
matchGroupsMaybe :: T.Text -> Regex -> [T.Text]
|
||||||
matchGroupsMaybe q re = case regexec re q of
|
matchGroupsMaybe q re = case regexec re q of
|
||||||
|
|
|
@ -26,6 +26,8 @@ import Text.Regex.TDFA
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- database cache types
|
-- database cache types
|
||||||
|
|
||||||
|
type MonadFinance = MonadReader ConfigState
|
||||||
|
|
||||||
data DeleteTxs = DeleteTxs
|
data DeleteTxs = DeleteTxs
|
||||||
{ dtTxs :: ![TransactionRId]
|
{ dtTxs :: ![TransactionRId]
|
||||||
, dtEntrySets :: ![EntrySetRId]
|
, dtEntrySets :: ![EntrySetRId]
|
||||||
|
@ -36,6 +38,7 @@ data DeleteTxs = DeleteTxs
|
||||||
|
|
||||||
type CDOps c d = CRUDOps [c] () () [d]
|
type CDOps c d = CRUDOps [c] () () [d]
|
||||||
|
|
||||||
|
-- TODO split the entry stuff from the account metadata stuff
|
||||||
data ConfigState = ConfigState
|
data ConfigState = ConfigState
|
||||||
{ csCurrencies :: !(CDOps (Entity CurrencyR) CurrencyRId)
|
{ csCurrencies :: !(CDOps (Entity CurrencyR) CurrencyRId)
|
||||||
, csAccounts :: !(CDOps (Entity AccountR) AccountRId)
|
, csAccounts :: !(CDOps (Entity AccountR) AccountRId)
|
||||||
|
@ -142,8 +145,6 @@ data EntryCRU
|
||||||
| ToRead ReadEntry
|
| ToRead ReadEntry
|
||||||
| ToInsert (Tx CommitR)
|
| ToInsert (Tx CommitR)
|
||||||
|
|
||||||
type MonadFinance = MonadReader ConfigState
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
-- misc
|
-- misc
|
||||||
|
|
||||||
|
@ -240,8 +241,6 @@ data MatchRes a = MatchPass !a | MatchFail | MatchSkip
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- exception types
|
-- exception types
|
||||||
|
|
||||||
data BalanceType = TooFewEntries | NotOneBlank deriving (Show)
|
|
||||||
|
|
||||||
data MatchType = MatchNumeric | MatchText deriving (Show)
|
data MatchType = MatchNumeric | MatchText deriving (Show)
|
||||||
|
|
||||||
data EntryIDType = AcntField | CurField | TagField deriving (Show)
|
data EntryIDType = AcntField | CurField | TagField deriving (Show)
|
||||||
|
@ -253,42 +252,49 @@ data LookupSuberr
|
||||||
| DBKey !EntryIDType
|
| DBKey !EntryIDType
|
||||||
deriving (Show)
|
deriving (Show)
|
||||||
|
|
||||||
data AllocationSuberr
|
|
||||||
= NoAllocations
|
|
||||||
| ExceededTotal
|
|
||||||
| MissingBlank
|
|
||||||
| TooManyBlanks
|
|
||||||
deriving (Show)
|
|
||||||
|
|
||||||
data PatternSuberr = ZeroLength | ZeroRepeats deriving (Show)
|
data PatternSuberr = ZeroLength | ZeroRepeats deriving (Show)
|
||||||
|
|
||||||
data InsertError
|
data DBLinkSubError
|
||||||
|
= DBLinkNoScale
|
||||||
|
| DBLinkNoValue
|
||||||
|
| DBLinkInvalidValue !Rational !Bool
|
||||||
|
| DBLinkInvalidBalance
|
||||||
|
| DBLinkInvalidPercent
|
||||||
|
deriving (Show)
|
||||||
|
|
||||||
|
data DBSubError
|
||||||
|
= DBShouldBeEmpty
|
||||||
|
| DBMultiScope
|
||||||
|
| DBUpdateUnbalanced
|
||||||
|
| DBLinkError !EntryRId !DBLinkSubError
|
||||||
|
deriving (Show)
|
||||||
|
|
||||||
|
data AppError
|
||||||
= RegexError !T.Text
|
= RegexError !T.Text
|
||||||
| MatchValPrecisionError !Natural !Natural
|
| MatchValPrecisionError !Natural !Natural
|
||||||
| AccountError !AcntID !(NE.NonEmpty AcntType)
|
| AccountTypeError !AcntID !(NE.NonEmpty AcntType)
|
||||||
| InsertIOError !T.Text
|
| StatementIOError !T.Text
|
||||||
| ParseError !T.Text
|
| ParseError !T.Text
|
||||||
| ConversionError !T.Text
|
| ConversionError !T.Text !Bool
|
||||||
| IndexError !(Entry AcntID LinkedNumGetter TagID) !Day
|
|
||||||
| RoundError !CurID
|
|
||||||
| LookupError !LookupSuberr !T.Text
|
| LookupError !LookupSuberr !T.Text
|
||||||
| IncomeError !Day !T.Text !Rational
|
| DatePatternError !Natural !Natural !(Maybe Natural) !PatternSuberr
|
||||||
| PatternError !Natural !Natural !(Maybe Natural) !PatternSuberr
|
|
||||||
| DaySpanError !Gregorian !(Maybe Gregorian)
|
| DaySpanError !Gregorian !(Maybe Gregorian)
|
||||||
| StatementError ![TxRecord] ![MatchRe]
|
| StatementError ![TxRecord] ![MatchRe]
|
||||||
| PeriodError !Day !Day
|
| PeriodError !Day !Day
|
||||||
|
| LinkError !EntryIndex !EntryIndex
|
||||||
|
| DBError !DBSubError
|
||||||
deriving (Show)
|
deriving (Show)
|
||||||
|
|
||||||
newtype InsertException = InsertException [InsertError]
|
newtype AppException = AppException [AppError]
|
||||||
deriving (Show, Semigroup) via [InsertError]
|
deriving (Show, Semigroup) via [AppError]
|
||||||
|
|
||||||
instance Exception InsertException
|
instance Exception AppException
|
||||||
|
|
||||||
type MonadInsertError = MonadError InsertException
|
type MonadAppError = MonadError AppException
|
||||||
|
|
||||||
type InsertExceptT = ExceptT InsertException
|
type AppExceptT = ExceptT AppException
|
||||||
|
|
||||||
type InsertExcept = InsertExceptT Identity
|
type AppExcept = AppExceptT Identity
|
||||||
|
|
||||||
type MatchRe = StatementParser (T.Text, Regex)
|
type MatchRe = StatementParser (T.Text, Regex)
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ module Internal.Utils
|
||||||
, resolveDaySpan
|
, resolveDaySpan
|
||||||
, resolveDaySpan_
|
, resolveDaySpan_
|
||||||
, intersectDaySpan
|
, intersectDaySpan
|
||||||
|
, throwAppError
|
||||||
, liftInner
|
, liftInner
|
||||||
, liftExceptT
|
, liftExceptT
|
||||||
, liftExcept
|
, liftExcept
|
||||||
|
@ -28,7 +29,6 @@ module Internal.Utils
|
||||||
, mapErrorsIO
|
, mapErrorsIO
|
||||||
, mapErrorsPooledIO
|
, mapErrorsPooledIO
|
||||||
, showError
|
, showError
|
||||||
, tshow
|
|
||||||
, lookupErr
|
, lookupErr
|
||||||
, uncurry3
|
, uncurry3
|
||||||
, dateMatches
|
, dateMatches
|
||||||
|
@ -49,7 +49,7 @@ module Internal.Utils
|
||||||
, entryPair
|
, entryPair
|
||||||
, singleQuote
|
, singleQuote
|
||||||
, keyVals
|
, keyVals
|
||||||
, realFracToDecimal'
|
, realFracToDecimalP
|
||||||
, roundToP
|
, roundToP
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
@ -58,6 +58,7 @@ import Control.Monad.Error.Class
|
||||||
import Control.Monad.Except
|
import Control.Monad.Except
|
||||||
import Data.Decimal
|
import Data.Decimal
|
||||||
import Data.Time.Format.ISO8601
|
import Data.Time.Format.ISO8601
|
||||||
|
import qualified Database.Esqueleto.Experimental as E
|
||||||
import GHC.Real
|
import GHC.Real
|
||||||
import Internal.Types.Main
|
import Internal.Types.Main
|
||||||
import RIO
|
import RIO
|
||||||
|
@ -72,7 +73,7 @@ import qualified RIO.Vector as V
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- intervals
|
-- intervals
|
||||||
|
|
||||||
expandDatePat :: DaySpan -> DatePat -> InsertExcept [Day]
|
expandDatePat :: DaySpan -> DatePat -> AppExcept [Day]
|
||||||
expandDatePat b (Cron cp) = expandCronPat b cp
|
expandDatePat b (Cron cp) = expandCronPat b cp
|
||||||
expandDatePat i (Mod mp) = return $ expandModPat mp i
|
expandDatePat i (Mod mp) = return $ expandModPat mp i
|
||||||
|
|
||||||
|
@ -91,7 +92,7 @@ expandModPat ModPat {mpStart = s, mpBy = b, mpUnit = u, mpRepeats = r} bs =
|
||||||
Month -> addGregorianMonthsClip
|
Month -> addGregorianMonthsClip
|
||||||
Year -> addGregorianYearsClip
|
Year -> addGregorianYearsClip
|
||||||
|
|
||||||
expandCronPat :: DaySpan -> CronPat -> InsertExcept [Day]
|
expandCronPat :: DaySpan -> CronPat -> AppExcept [Day]
|
||||||
expandCronPat b CronPat {cpYear, cpMonth, cpDay, cpWeekly} =
|
expandCronPat b CronPat {cpYear, cpMonth, cpDay, cpWeekly} =
|
||||||
combineError3 yRes mRes dRes $ \ys ms ds ->
|
combineError3 yRes mRes dRes $ \ys ms ds ->
|
||||||
filter validWeekday $
|
filter validWeekday $
|
||||||
|
@ -122,14 +123,14 @@ expandCronPat b CronPat {cpYear, cpMonth, cpDay, cpWeekly} =
|
||||||
| m `elem` [4, 6, 9, 11] && d > 30 = Nothing
|
| m `elem` [4, 6, 9, 11] && d > 30 = Nothing
|
||||||
| otherwise = Just $ fromGregorian y m d
|
| otherwise = Just $ fromGregorian y m d
|
||||||
|
|
||||||
expandMDYPat :: Natural -> Natural -> MDYPat -> InsertExcept [Natural]
|
expandMDYPat :: Natural -> Natural -> MDYPat -> AppExcept [Natural]
|
||||||
expandMDYPat lower upper (Single x) = return [x | lower <= x && x <= upper]
|
expandMDYPat lower upper (Single x) = return [x | lower <= x && x <= upper]
|
||||||
expandMDYPat lower upper (Multi xs) = return $ dropWhile (<= lower) $ takeWhile (<= upper) xs
|
expandMDYPat lower upper (Multi xs) = return $ dropWhile (<= lower) $ takeWhile (<= upper) xs
|
||||||
expandMDYPat lower upper (After x) = return [max lower x .. upper]
|
expandMDYPat lower upper (After x) = return [max lower x .. upper]
|
||||||
expandMDYPat lower upper (Before x) = return [lower .. min upper x]
|
expandMDYPat lower upper (Before x) = return [lower .. min upper x]
|
||||||
expandMDYPat lower upper (Between x y) = return [max lower x .. min upper y]
|
expandMDYPat lower upper (Between x y) = return [max lower x .. min upper y]
|
||||||
expandMDYPat lower upper (Repeat RepeatPat {rpStart = s, rpBy = b, rpRepeats = r})
|
expandMDYPat lower upper (Repeat RepeatPat {rpStart = s, rpBy = b, rpRepeats = r})
|
||||||
| b < 1 = throwError $ InsertException [PatternError s b r ZeroLength]
|
| b < 1 = throwAppError $ DatePatternError s b r ZeroLength
|
||||||
| otherwise = do
|
| otherwise = do
|
||||||
k <- limit r
|
k <- limit r
|
||||||
return $ dropWhile (<= lower) $ takeWhile (<= k) [s + i * b | i <- [0 ..]]
|
return $ dropWhile (<= lower) $ takeWhile (<= k) [s + i * b | i <- [0 ..]]
|
||||||
|
@ -138,14 +139,14 @@ expandMDYPat lower upper (Repeat RepeatPat {rpStart = s, rpBy = b, rpRepeats = r
|
||||||
limit (Just n)
|
limit (Just n)
|
||||||
-- this guard not only produces the error for the user but also protects
|
-- this guard not only produces the error for the user but also protects
|
||||||
-- from an underflow below it
|
-- from an underflow below it
|
||||||
| n < 1 = throwError $ InsertException [PatternError s b r ZeroRepeats]
|
| n < 1 = throwAppError $ DatePatternError s b r ZeroRepeats
|
||||||
| otherwise = return $ min (s + b * (n - 1)) upper
|
| otherwise = return $ min (s + b * (n - 1)) upper
|
||||||
|
|
||||||
dayToWeekday :: Day -> Int
|
dayToWeekday :: Day -> Int
|
||||||
dayToWeekday (ModifiedJulianDay d) = mod (fromIntegral d + 2) 7
|
dayToWeekday (ModifiedJulianDay d) = mod (fromIntegral d + 2) 7
|
||||||
|
|
||||||
askDays
|
askDays
|
||||||
:: (MonadFinance m, MonadInsertError m)
|
:: (MonadFinance m, MonadAppError m)
|
||||||
=> DatePat
|
=> DatePat
|
||||||
-> Maybe Interval
|
-> Maybe Interval
|
||||||
-> m [Day]
|
-> m [Day]
|
||||||
|
@ -224,7 +225,7 @@ inDaySpan bs = withinDays (fromDaySpan bs)
|
||||||
withinDays :: (Day, Day) -> Day -> Bool
|
withinDays :: (Day, Day) -> Day -> Bool
|
||||||
withinDays (d0, d1) x = d0 <= x && x < d1
|
withinDays (d0, d1) x = d0 <= x && x < d1
|
||||||
|
|
||||||
resolveDaySpan :: Interval -> InsertExcept DaySpan
|
resolveDaySpan :: Interval -> AppExcept DaySpan
|
||||||
resolveDaySpan i@Interval {intStart = s} =
|
resolveDaySpan i@Interval {intStart = s} =
|
||||||
resolveDaySpan_ (s {gYear = gYear s + 50}) i
|
resolveDaySpan_ (s {gYear = gYear s + 50}) i
|
||||||
|
|
||||||
|
@ -237,14 +238,14 @@ intersectDaySpan a b =
|
||||||
a' = max a0 a1
|
a' = max a0 a1
|
||||||
b' = min b0 b1
|
b' = min b0 b1
|
||||||
|
|
||||||
resolveDaySpan_ :: Gregorian -> Interval -> InsertExcept DaySpan
|
resolveDaySpan_ :: Gregorian -> Interval -> AppExcept DaySpan
|
||||||
resolveDaySpan_ def Interval {intStart = s, intEnd = e} =
|
resolveDaySpan_ def Interval {intStart = s, intEnd = e} =
|
||||||
-- TODO the default isn't checked here :/
|
-- TODO the default isn't checked here :/
|
||||||
case fromGregorian' <$> e of
|
case fromGregorian' <$> e of
|
||||||
Nothing -> return $ toDaySpan_ $ fromGregorian' def
|
Nothing -> return $ toDaySpan_ $ fromGregorian' def
|
||||||
Just e_
|
Just e_
|
||||||
| s_ < e_ -> return $ toDaySpan_ e_
|
| s_ < e_ -> return $ toDaySpan_ e_
|
||||||
| otherwise -> throwError $ InsertException [DaySpanError s e]
|
| otherwise -> throwAppError $ DaySpanError s e
|
||||||
where
|
where
|
||||||
s_ = fromGregorian' s
|
s_ = fromGregorian' s
|
||||||
toDaySpan_ end = toDaySpan (s_, end)
|
toDaySpan_ end = toDaySpan (s_, end)
|
||||||
|
@ -259,9 +260,9 @@ toDaySpan (a, b) = (a, fromIntegral $ diffDays b a - 1)
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- matching
|
-- matching
|
||||||
|
|
||||||
valMatches :: ValMatcher -> Rational -> InsertExcept Bool
|
valMatches :: ValMatcher -> Rational -> AppExcept Bool
|
||||||
valMatches ValMatcher {vmDen, vmSign, vmNum, vmPrec} x
|
valMatches ValMatcher {vmDen, vmSign, vmNum, vmPrec} x
|
||||||
| Just d_ <- vmDen, d_ >= p = throwError $ InsertException [MatchValPrecisionError d_ p]
|
| Just d_ <- vmDen, d_ >= p = throwAppError $ MatchValPrecisionError d_ p
|
||||||
| otherwise =
|
| otherwise =
|
||||||
return $
|
return $
|
||||||
checkMaybe (s ==) vmSign
|
checkMaybe (s ==) vmSign
|
||||||
|
@ -279,6 +280,9 @@ dateMatches md = (EQ ==) . compareDate md
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- error flow control
|
-- error flow control
|
||||||
|
|
||||||
|
throwAppError :: MonadAppError m => AppError -> m a
|
||||||
|
throwAppError e = throwError $ AppException [e]
|
||||||
|
|
||||||
liftInner :: Monad m => ExceptT e Identity a -> ExceptT e m a
|
liftInner :: Monad m => ExceptT e Identity a -> ExceptT e m a
|
||||||
liftInner = mapExceptT (return . runIdentity)
|
liftInner = mapExceptT (return . runIdentity)
|
||||||
|
|
||||||
|
@ -288,41 +292,37 @@ liftExceptT x = runExceptT x >>= either throwError return
|
||||||
liftExcept :: MonadError e m => Except e a -> m a
|
liftExcept :: MonadError e m => Except e a -> m a
|
||||||
liftExcept = either throwError return . runExcept
|
liftExcept = either throwError return . runExcept
|
||||||
|
|
||||||
liftIOExceptT :: MonadIO m => InsertExceptT m a -> m a
|
liftIOExceptT :: MonadIO m => AppExceptT m a -> m a
|
||||||
liftIOExceptT = fromEither <=< runExceptT
|
liftIOExceptT = fromEither <=< runExceptT
|
||||||
|
|
||||||
liftIOExcept :: MonadIO m => InsertExcept a -> m a
|
liftIOExcept :: MonadIO m => AppExcept a -> m a
|
||||||
liftIOExcept = fromEither . runExcept
|
liftIOExcept = fromEither . runExcept
|
||||||
|
|
||||||
combineError :: MonadError InsertException m => m a -> m b -> (a -> b -> c) -> m c
|
combineError :: MonadAppError m => m a -> m b -> (a -> b -> c) -> m c
|
||||||
combineError a b f = combineErrorM a b (\x y -> pure $ f x y)
|
combineError a b f = combineErrorM a b (\x y -> pure $ f x y)
|
||||||
|
|
||||||
combineError_ :: MonadError InsertException m => m a -> m b -> m ()
|
combineError_ :: MonadAppError m => m a -> m b -> m ()
|
||||||
combineError_ a b = do
|
combineError_ a b = do
|
||||||
_ <- catchError a $ \e ->
|
_ <- catchError a $ \e ->
|
||||||
throwError =<< catchError (e <$ b) (return . (e <>))
|
throwError =<< catchError (e <$ b) (return . (e <>))
|
||||||
_ <- b
|
_ <- b
|
||||||
return ()
|
return ()
|
||||||
|
|
||||||
combineErrorM :: MonadError InsertException m => m a -> m b -> (a -> b -> m c) -> m c
|
combineErrorM :: MonadAppError m => m a -> m b -> (a -> b -> m c) -> m c
|
||||||
combineErrorM a b f = do
|
combineErrorM a b f = do
|
||||||
a' <- catchError a $ \e ->
|
a' <- catchError a $ \e ->
|
||||||
throwError =<< catchError (e <$ b) (return . (e <>))
|
throwError =<< catchError (e <$ b) (return . (e <>))
|
||||||
f a' =<< b
|
f a' =<< b
|
||||||
|
|
||||||
combineError3 :: MonadError InsertException m => m a -> m b -> m c -> (a -> b -> c -> d) -> m d
|
combineError3 :: MonadAppError m => m a -> m b -> m c -> (a -> b -> c -> d) -> m d
|
||||||
combineError3 a b c f =
|
combineError3 a b c f =
|
||||||
combineError (combineError a b (,)) c $ \(x, y) z -> f x y z
|
combineError (combineError a b (,)) c $ \(x, y) z -> f x y z
|
||||||
|
|
||||||
combineErrorM3 :: MonadError InsertException m => m a -> m b -> m c -> (a -> b -> c -> m d) -> m d
|
combineErrorM3 :: MonadAppError m => m a -> m b -> m c -> (a -> b -> c -> m d) -> m d
|
||||||
combineErrorM3 a b c f = do
|
combineErrorM3 a b c f = do
|
||||||
combineErrorM (combineErrorM a b (curry return)) c $ \(x, y) z -> f x y z
|
combineErrorM (combineErrorM a b (curry return)) c $ \(x, y) z -> f x y z
|
||||||
|
|
||||||
mapErrors
|
mapErrors :: (Traversable t, MonadAppError m) => (a -> m b) -> t a -> m (t b)
|
||||||
:: (Traversable t, MonadError InsertException m)
|
|
||||||
=> (a -> m b)
|
|
||||||
-> t a
|
|
||||||
-> m (t b)
|
|
||||||
-- First, record number of each action. Then try each action. On first failure,
|
-- First, record number of each action. Then try each action. On first failure,
|
||||||
-- note it's position in the sequence, skip ahead to the untried actions,
|
-- note it's position in the sequence, skip ahead to the untried actions,
|
||||||
-- collect failures and add to the first failure.
|
-- collect failures and add to the first failure.
|
||||||
|
@ -333,7 +333,7 @@ mapErrors f xs = mapM go $ enumTraversable xs
|
||||||
throwError $ foldr (<>) e es
|
throwError $ foldr (<>) e es
|
||||||
err x = catchError (Nothing <$ x) (pure . Just)
|
err x = catchError (Nothing <$ x) (pure . Just)
|
||||||
|
|
||||||
combineErrors :: (Traversable t, MonadError InsertException m) => t (m a) -> m (t a)
|
combineErrors :: (Traversable t, MonadAppError m) => t (m a) -> m (t a)
|
||||||
combineErrors = mapErrors id
|
combineErrors = mapErrors id
|
||||||
|
|
||||||
enumTraversable :: (Num n, Traversable t) => t a -> t (n, a)
|
enumTraversable :: (Num n, Traversable t) => t a -> t (n, a)
|
||||||
|
@ -349,9 +349,9 @@ combineErrorIO3 a b c f = combineErrorIOM3 a b c (\x y z -> pure $ f x y z)
|
||||||
|
|
||||||
combineErrorIOM2 :: MonadUnliftIO m => m a -> m b -> (a -> b -> m c) -> m c
|
combineErrorIOM2 :: MonadUnliftIO m => m a -> m b -> (a -> b -> m c) -> m c
|
||||||
combineErrorIOM2 a b f = do
|
combineErrorIOM2 a b f = do
|
||||||
a' <- catch a $ \(InsertException es) ->
|
a' <- catch a $ \(AppException es) ->
|
||||||
(throwIO . InsertException)
|
(throwIO . AppException)
|
||||||
=<< catch (es <$ b) (\(InsertException es') -> return (es' ++ es))
|
=<< catch (es <$ b) (\(AppException es') -> return (es' ++ es))
|
||||||
f a' =<< b
|
f a' =<< b
|
||||||
|
|
||||||
combineErrorIOM3 :: MonadUnliftIO m => m a -> m b -> m c -> (a -> b -> c -> m d) -> m d
|
combineErrorIOM3 :: MonadUnliftIO m => m a -> m b -> m c -> (a -> b -> c -> m d) -> m d
|
||||||
|
@ -361,39 +361,39 @@ combineErrorIOM3 a b c f =
|
||||||
mapErrorsPooledIO :: (Traversable t, MonadUnliftIO m) => Int -> (a -> m b) -> t a -> m (t b)
|
mapErrorsPooledIO :: (Traversable t, MonadUnliftIO m) => Int -> (a -> m b) -> t a -> m (t b)
|
||||||
mapErrorsPooledIO t f xs = pooledMapConcurrentlyN t go $ enumTraversable xs
|
mapErrorsPooledIO t f xs = pooledMapConcurrentlyN t go $ enumTraversable xs
|
||||||
where
|
where
|
||||||
go (n, x) = catch (f x) $ \(InsertException e) -> do
|
go (n, x) = catch (f x) $ \(AppException e) -> do
|
||||||
es <- fmap catMaybes $ mapM (err . f) $ drop (n + 1) $ toList xs
|
es <- fmap catMaybes $ mapM (err . f) $ drop (n + 1) $ toList xs
|
||||||
throwIO $ InsertException $ foldr (<>) e es
|
throwIO $ AppException $ foldr (<>) e es
|
||||||
err x = catch (Nothing <$ x) $ \(InsertException es) -> pure $ Just es
|
err x = catch (Nothing <$ x) $ \(AppException es) -> pure $ Just es
|
||||||
|
|
||||||
mapErrorsIO :: (Traversable t, MonadUnliftIO m) => (a -> m b) -> t a -> m (t b)
|
mapErrorsIO :: (Traversable t, MonadUnliftIO m) => (a -> m b) -> t a -> m (t b)
|
||||||
mapErrorsIO f xs = mapM go $ enumTraversable xs
|
mapErrorsIO f xs = mapM go $ enumTraversable xs
|
||||||
where
|
where
|
||||||
go (n, x) = catch (f x) $ \(InsertException e) -> do
|
go (n, x) = catch (f x) $ \(AppException e) -> do
|
||||||
es <- fmap catMaybes $ mapM (err . f) $ drop (n + 1) $ toList xs
|
es <- fmap catMaybes $ mapM (err . f) $ drop (n + 1) $ toList xs
|
||||||
throwIO $ InsertException $ foldr (<>) e es
|
throwIO $ AppException $ foldr (<>) e es
|
||||||
err x = catch (Nothing <$ x) $ \(InsertException es) -> pure $ Just es
|
err x = catch (Nothing <$ x) $ \(AppException es) -> pure $ Just es
|
||||||
|
|
||||||
collectErrorsIO :: (Traversable t, MonadUnliftIO m) => t (m a) -> m (t a)
|
collectErrorsIO :: (Traversable t, MonadUnliftIO m) => t (m a) -> m (t a)
|
||||||
collectErrorsIO = mapErrorsIO id
|
collectErrorsIO = mapErrorsIO id
|
||||||
|
|
||||||
lookupErr :: (Ord k, Show k) => LookupSuberr -> k -> M.Map k v -> InsertExcept v
|
lookupErr :: (Ord k, Show k) => LookupSuberr -> k -> M.Map k v -> AppExcept v
|
||||||
lookupErr what k m = case M.lookup k m of
|
lookupErr what k m = case M.lookup k m of
|
||||||
Just x -> return x
|
Just x -> return x
|
||||||
_ -> throwError $ InsertException [LookupError what $ tshow k]
|
_ -> throwAppError $ LookupError what $ tshow k
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- error display
|
-- error display
|
||||||
|
|
||||||
showError :: InsertError -> [T.Text]
|
showError :: AppError -> [T.Text]
|
||||||
showError other = case other of
|
showError other = case other of
|
||||||
(StatementError ts ms) -> (tshowx <$> ts) ++ (showMatch <$> ms)
|
(StatementError ts ms) -> (showTx <$> ts) ++ (showMatch <$> ms)
|
||||||
(DaySpanError a b) ->
|
(DaySpanError a b) ->
|
||||||
[T.unwords ["Could not create bounds from", showGregorian_ a, "and", showGreg b]]
|
[T.unwords ["Could not create bounds from", showGregorian_ a, "and", showGreg b]]
|
||||||
where
|
where
|
||||||
showGreg (Just g) = showGregorian_ g
|
showGreg (Just g) = showGregorian_ g
|
||||||
showGreg Nothing = "Inf"
|
showGreg Nothing = "Inf"
|
||||||
(AccountError a ts) ->
|
(AccountTypeError a ts) ->
|
||||||
[ T.unwords
|
[ T.unwords
|
||||||
[ "account type of key"
|
[ "account type of key"
|
||||||
, singleQuote $ unAcntID a
|
, singleQuote $ unAcntID a
|
||||||
|
@ -403,7 +403,7 @@ showError other = case other of
|
||||||
]
|
]
|
||||||
where
|
where
|
||||||
ts_ = T.intercalate ", " $ NE.toList $ fmap atName ts
|
ts_ = T.intercalate ", " $ NE.toList $ fmap atName ts
|
||||||
(PatternError s b r p) -> [T.unwords [msg, "in pattern: ", pat]]
|
(DatePatternError s b r p) -> [T.unwords [msg, "in pattern: ", pat]]
|
||||||
where
|
where
|
||||||
pat =
|
pat =
|
||||||
keyVals $
|
keyVals $
|
||||||
|
@ -418,8 +418,15 @@ showError other = case other of
|
||||||
ZeroLength -> "Zero repeat length"
|
ZeroLength -> "Zero repeat length"
|
||||||
ZeroRepeats -> "Zero repeats"
|
ZeroRepeats -> "Zero repeats"
|
||||||
(RegexError re) -> [T.append "could not make regex from pattern: " re]
|
(RegexError re) -> [T.append "could not make regex from pattern: " re]
|
||||||
(ConversionError x) -> [T.append "Could not convert to rational number: " x]
|
(ConversionError x isDouble) ->
|
||||||
(InsertIOError msg) -> [T.append "IO Error: " msg]
|
[ T.unwords
|
||||||
|
[ "Could not convert to"
|
||||||
|
, if isDouble then "double" else "rational"
|
||||||
|
, "number: "
|
||||||
|
, x
|
||||||
|
]
|
||||||
|
]
|
||||||
|
(StatementIOError msg) -> [T.append "IO Error: " msg]
|
||||||
(ParseError msg) -> [T.append "Parse Error: " msg]
|
(ParseError msg) -> [T.append "Parse Error: " msg]
|
||||||
(MatchValPrecisionError d p) ->
|
(MatchValPrecisionError d p) ->
|
||||||
[T.unwords ["Match denominator", tshow d, "must be less than", tshow p]]
|
[T.unwords ["Match denominator", tshow d, "must be less than", tshow p]]
|
||||||
|
@ -437,16 +444,6 @@ showError other = case other of
|
||||||
idName TagField = "tag"
|
idName TagField = "tag"
|
||||||
matchName MatchNumeric = "numeric"
|
matchName MatchNumeric = "numeric"
|
||||||
matchName MatchText = "text"
|
matchName MatchText = "text"
|
||||||
(IncomeError day name balance) ->
|
|
||||||
[ T.unwords
|
|
||||||
[ "Income allocations for budget"
|
|
||||||
, singleQuote name
|
|
||||||
, "exceed total on day"
|
|
||||||
, tshow day
|
|
||||||
, "where balance is"
|
|
||||||
, tshow (fromRational balance :: Double)
|
|
||||||
]
|
|
||||||
]
|
|
||||||
(PeriodError start next) ->
|
(PeriodError start next) ->
|
||||||
[ T.unwords
|
[ T.unwords
|
||||||
[ "First pay period on "
|
[ "First pay period on "
|
||||||
|
@ -455,28 +452,40 @@ showError other = case other of
|
||||||
, singleQuote $ tshow next
|
, singleQuote $ tshow next
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
(IndexError Entry {eValue = LinkedNumGetter {lngIndex}, eAcnt} day) ->
|
(LinkError i m) ->
|
||||||
[ T.unwords
|
[ T.unwords
|
||||||
[ "No credit entry for index"
|
[ "entry index"
|
||||||
, singleQuote $ tshow lngIndex
|
, singleQuote $ tshow i
|
||||||
, "for entry with account"
|
, "out of range: max index is"
|
||||||
, singleQuote $ unAcntID eAcnt
|
, singleQuote $ tshow m
|
||||||
, "on"
|
|
||||||
, tshow day
|
|
||||||
]
|
|
||||||
]
|
|
||||||
(RoundError cur) ->
|
|
||||||
[ T.unwords
|
|
||||||
[ "Could not look up precision for currency"
|
|
||||||
, singleQuote $ unCurID cur
|
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
(DBError d) -> case d of
|
||||||
|
DBShouldBeEmpty -> ["database has no rows in 'config_state' but has other data"]
|
||||||
|
DBMultiScope -> ["database has multiple rows in 'config_state'"]
|
||||||
|
DBUpdateUnbalanced -> ["update is missing debit or credit entries"]
|
||||||
|
DBLinkError k l ->
|
||||||
|
let k' = T.append "in entry key: " $ tshow $ E.fromSqlKey k
|
||||||
|
in case l of
|
||||||
|
DBLinkNoScale -> [T.append "no link scale" k']
|
||||||
|
DBLinkNoValue -> [T.append "no link value" k']
|
||||||
|
DBLinkInvalidValue v isfixed ->
|
||||||
|
[ T.unwords
|
||||||
|
[ if isfixed
|
||||||
|
then "fixed link should not have value"
|
||||||
|
else "untyped value is ambiguous"
|
||||||
|
, singleQuote $ tshow v
|
||||||
|
, k'
|
||||||
|
]
|
||||||
|
]
|
||||||
|
DBLinkInvalidBalance -> [T.append "no value given for balance link" k']
|
||||||
|
DBLinkInvalidPercent -> [T.append "no value given for percent link" k']
|
||||||
|
|
||||||
showGregorian_ :: Gregorian -> T.Text
|
showGregorian_ :: Gregorian -> T.Text
|
||||||
showGregorian_ Gregorian {gYear, gMonth, gDay} = T.intercalate "-" $ tshow <$> [gYear, gMonth, gDay]
|
showGregorian_ Gregorian {gYear, gMonth, gDay} = T.intercalate "-" $ tshow <$> [gYear, gMonth, gDay]
|
||||||
|
|
||||||
tshowx :: TxRecord -> T.Text
|
showTx :: TxRecord -> T.Text
|
||||||
tshowx TxRecord {trDate = d, trAmount = v, trDesc = e, trFile = f} =
|
showTx TxRecord {trDate = d, trAmount = v, trDesc = e, trFile = f} =
|
||||||
T.append "Unmatched transaction: " $
|
T.append "Unmatched transaction: " $
|
||||||
keyVals
|
keyVals
|
||||||
[ ("path", T.pack f)
|
[ ("path", T.pack f)
|
||||||
|
@ -589,29 +598,29 @@ mapAdd_ k v = M.alter (maybe (Just v) (Just . (+ v))) k
|
||||||
uncurry3 :: (a -> b -> c -> d) -> (a, b, c) -> d
|
uncurry3 :: (a -> b -> c -> d) -> (a, b, c) -> d
|
||||||
uncurry3 f (a, b, c) = f a b c
|
uncurry3 f (a, b, c) = f a b c
|
||||||
|
|
||||||
lookupAccount :: (MonadInsertError m, MonadFinance m) => AcntID -> m (AccountRId, AcntType)
|
lookupAccount :: (MonadAppError m, MonadFinance m) => AcntID -> m (AccountRId, AcntType)
|
||||||
lookupAccount = lookupFinance AcntField csAccountMap
|
lookupAccount = lookupFinance AcntField csAccountMap
|
||||||
|
|
||||||
lookupAccountKey :: (MonadInsertError m, MonadFinance m) => AcntID -> m AccountRId
|
lookupAccountKey :: (MonadAppError m, MonadFinance m) => AcntID -> m AccountRId
|
||||||
lookupAccountKey = fmap fst . lookupAccount
|
lookupAccountKey = fmap fst . lookupAccount
|
||||||
|
|
||||||
lookupAccountType :: (MonadInsertError m, MonadFinance m) => AcntID -> m AcntType
|
lookupAccountType :: (MonadAppError m, MonadFinance m) => AcntID -> m AcntType
|
||||||
lookupAccountType = fmap snd . lookupAccount
|
lookupAccountType = fmap snd . lookupAccount
|
||||||
|
|
||||||
lookupCurrency :: (MonadInsertError m, MonadFinance m) => CurID -> m CurrencyPrec
|
lookupCurrency :: (MonadAppError m, MonadFinance m) => CurID -> m CurrencyPrec
|
||||||
lookupCurrency = lookupFinance CurField csCurrencyMap
|
lookupCurrency = lookupFinance CurField csCurrencyMap
|
||||||
|
|
||||||
lookupCurrencyKey :: (MonadInsertError m, MonadFinance m) => CurID -> m CurrencyRId
|
lookupCurrencyKey :: (MonadAppError m, MonadFinance m) => CurID -> m CurrencyRId
|
||||||
lookupCurrencyKey = fmap cpID . lookupCurrency
|
lookupCurrencyKey = fmap cpID . lookupCurrency
|
||||||
|
|
||||||
lookupCurrencyPrec :: (MonadInsertError m, MonadFinance m) => CurID -> m Precision
|
lookupCurrencyPrec :: (MonadAppError m, MonadFinance m) => CurID -> m Precision
|
||||||
lookupCurrencyPrec = fmap cpPrec . lookupCurrency
|
lookupCurrencyPrec = fmap cpPrec . lookupCurrency
|
||||||
|
|
||||||
lookupTag :: (MonadInsertError m, MonadFinance m) => TagID -> m TagRId
|
lookupTag :: (MonadAppError m, MonadFinance m) => TagID -> m TagRId
|
||||||
lookupTag = lookupFinance TagField csTagMap
|
lookupTag = lookupFinance TagField csTagMap
|
||||||
|
|
||||||
lookupFinance
|
lookupFinance
|
||||||
:: (MonadInsertError m, MonadFinance m, Ord k, Show k)
|
:: (MonadAppError m, MonadFinance m, Ord k, Show k)
|
||||||
=> EntryIDType
|
=> EntryIDType
|
||||||
-> (ConfigState -> M.Map k a)
|
-> (ConfigState -> M.Map k a)
|
||||||
-> k
|
-> k
|
||||||
|
@ -619,7 +628,7 @@ lookupFinance
|
||||||
lookupFinance t f c = (liftExcept . lookupErr (DBKey t) c) =<< asks f
|
lookupFinance t f c = (liftExcept . lookupErr (DBKey t) c) =<< asks f
|
||||||
|
|
||||||
balanceTxs
|
balanceTxs
|
||||||
:: (MonadInsertError m, MonadFinance m)
|
:: (MonadAppError m, MonadFinance m)
|
||||||
=> [EntryCRU]
|
=> [EntryCRU]
|
||||||
-> m ([UEBalanced], [InsertTx])
|
-> m ([UEBalanced], [InsertTx])
|
||||||
balanceTxs ebs =
|
balanceTxs ebs =
|
||||||
|
@ -796,7 +805,7 @@ updateUnknown k e = do
|
||||||
-- balancing
|
-- balancing
|
||||||
|
|
||||||
balancePrimaryEntrySet
|
balancePrimaryEntrySet
|
||||||
:: (MonadInsertError m, MonadFinance m)
|
:: (MonadAppError m, MonadFinance m)
|
||||||
=> BudgetName
|
=> BudgetName
|
||||||
-> PrimaryEntrySet
|
-> PrimaryEntrySet
|
||||||
-> StateT EntryBals m InsertEntrySet
|
-> StateT EntryBals m InsertEntrySet
|
||||||
|
@ -821,7 +830,7 @@ balancePrimaryEntrySet
|
||||||
balanceFinal bc (-esTotalValue) fs'' t0' ts'
|
balanceFinal bc (-esTotalValue) fs'' t0' ts'
|
||||||
|
|
||||||
balanceSecondaryEntrySet
|
balanceSecondaryEntrySet
|
||||||
:: (MonadInsertError m, MonadFinance m)
|
:: (MonadAppError m, MonadFinance m)
|
||||||
=> BudgetName
|
=> BudgetName
|
||||||
-> SecondayEntrySet
|
-> SecondayEntrySet
|
||||||
-> StateT EntryBals m InsertEntrySet
|
-> StateT EntryBals m InsertEntrySet
|
||||||
|
@ -846,7 +855,7 @@ balanceSecondaryEntrySet
|
||||||
bc = (esCurrency, budgetName)
|
bc = (esCurrency, budgetName)
|
||||||
|
|
||||||
balanceFinal
|
balanceFinal
|
||||||
:: (MonadInsertError m)
|
:: (MonadAppError m)
|
||||||
=> BCKey
|
=> BCKey
|
||||||
-> Decimal
|
-> Decimal
|
||||||
-> NonEmpty InsertEntry
|
-> NonEmpty InsertEntry
|
||||||
|
@ -865,7 +874,7 @@ balanceFinal k@(curID, _) tot fs t0 ts = do
|
||||||
}
|
}
|
||||||
|
|
||||||
balanceTotalEntrySet
|
balanceTotalEntrySet
|
||||||
:: (MonadInsertError m)
|
:: (MonadAppError m)
|
||||||
=> (ABCKey -> v -> StateT EntryBals m (Decimal, Maybe CachedEntry))
|
=> (ABCKey -> v -> StateT EntryBals m (Decimal, Maybe CachedEntry))
|
||||||
-> BCKey
|
-> BCKey
|
||||||
-> Decimal
|
-> Decimal
|
||||||
|
@ -890,19 +899,19 @@ liftInnerS :: Monad m => StateT e Identity a -> StateT e m a
|
||||||
liftInnerS = mapStateT (return . runIdentity)
|
liftInnerS = mapStateT (return . runIdentity)
|
||||||
|
|
||||||
balanceLinked
|
balanceLinked
|
||||||
:: MonadInsertError m
|
:: MonadAppError m
|
||||||
=> Vector Decimal
|
=> Vector Decimal
|
||||||
-> ABCKey
|
-> ABCKey
|
||||||
-> EntryLink
|
-> EntryLink
|
||||||
-> StateT EntryBals m (Decimal, Maybe CachedEntry)
|
-> StateT EntryBals m (Decimal, Maybe CachedEntry)
|
||||||
balanceLinked from k lg = case lg of
|
balanceLinked from k lg = case lg of
|
||||||
(LinkIndex LinkedNumGetter {lngIndex, lngScale}) -> do
|
(LinkIndex LinkedNumGetter {lngIndex, lngScale}) -> do
|
||||||
let res = fmap (go lngScale) $ from V.!? fromIntegral lngIndex
|
let i = fromIntegral lngIndex
|
||||||
|
upper = EntryIndex $ V.length from
|
||||||
|
res = fmap (go lngScale) $ from V.!? i
|
||||||
case res of
|
case res of
|
||||||
Just v -> return (v, Just $ CachedLink (EntryIndex $ fromIntegral lngIndex) (LinkScale lngScale))
|
Just v -> return (v, Just $ CachedLink (EntryIndex $ fromIntegral lngIndex) (LinkScale lngScale))
|
||||||
-- TODO this error would be much more informative if I had access to the
|
Nothing -> throwAppError $ LinkError (EntryIndex i) upper
|
||||||
-- file from which it came
|
|
||||||
Nothing -> throwError undefined
|
|
||||||
(LinkValue d) -> liftInnerS $ balanceDeferred k d
|
(LinkValue d) -> liftInnerS $ balanceDeferred k d
|
||||||
where
|
where
|
||||||
go s = negate . (*. s)
|
go s = negate . (*. s)
|
||||||
|
@ -917,7 +926,7 @@ balanceDeferred k e = do
|
||||||
return (newval, d)
|
return (newval, d)
|
||||||
|
|
||||||
balanceEntry
|
balanceEntry
|
||||||
:: (MonadInsertError m)
|
:: (MonadAppError m)
|
||||||
=> (ABCKey -> v -> StateT EntryBals m (Decimal, Maybe CachedEntry))
|
=> (ABCKey -> v -> StateT EntryBals m (Decimal, Maybe CachedEntry))
|
||||||
-> BCKey
|
-> BCKey
|
||||||
-> Entry AccountRId v TagRId
|
-> Entry AccountRId v TagRId
|
||||||
|
@ -932,7 +941,7 @@ balanceEntry f k e@Entry {eValue, eAcnt = acntID} = do
|
||||||
}
|
}
|
||||||
|
|
||||||
resolveAcntAndTags
|
resolveAcntAndTags
|
||||||
:: (MonadInsertError m, MonadFinance m)
|
:: (MonadAppError m, MonadFinance m)
|
||||||
=> Entry AcntID v TagID
|
=> Entry AcntID v TagID
|
||||||
-> m (Entry AccountRId v TagRId)
|
-> m (Entry AccountRId v TagRId)
|
||||||
resolveAcntAndTags e@Entry {eAcnt, eTags} = do
|
resolveAcntAndTags e@Entry {eAcnt, eTags} = do
|
||||||
|
@ -952,7 +961,7 @@ findBalance k e = do
|
||||||
-- transfers
|
-- transfers
|
||||||
|
|
||||||
expandTransfers
|
expandTransfers
|
||||||
:: (MonadInsertError m, MonadFinance m)
|
:: (MonadAppError m, MonadFinance m)
|
||||||
=> CommitR
|
=> CommitR
|
||||||
-> BudgetName
|
-> BudgetName
|
||||||
-> DaySpan
|
-> DaySpan
|
||||||
|
@ -961,7 +970,7 @@ expandTransfers
|
||||||
expandTransfers tc name bounds = fmap concat . mapErrors (expandTransfer tc name bounds)
|
expandTransfers tc name bounds = fmap concat . mapErrors (expandTransfer tc name bounds)
|
||||||
|
|
||||||
expandTransfer
|
expandTransfer
|
||||||
:: (MonadInsertError m, MonadFinance m)
|
:: (MonadAppError m, MonadFinance m)
|
||||||
=> CommitR
|
=> CommitR
|
||||||
-> BudgetName
|
-> BudgetName
|
||||||
-> DaySpan
|
-> DaySpan
|
||||||
|
@ -980,7 +989,7 @@ expandTransfer tc name bounds Transfer {transAmounts, transTo, transCurrency, tr
|
||||||
} = do
|
} = do
|
||||||
cp <- lookupCurrency transCurrency
|
cp <- lookupCurrency transCurrency
|
||||||
let v' = (-v)
|
let v' = (-v)
|
||||||
let dec = realFracToDecimal' (cpPrec cp) v'
|
let dec = realFracToDecimalP (cpPrec cp) v'
|
||||||
let v'' = case t of
|
let v'' = case t of
|
||||||
TFixed -> EntryFixed dec
|
TFixed -> EntryFixed dec
|
||||||
TPercent -> EntryPercent v'
|
TPercent -> EntryPercent v'
|
||||||
|
@ -1021,7 +1030,7 @@ entryPair (TaggedAcnt fa fts) (TaggedAcnt ta tts) curid com totval val1 =
|
||||||
}
|
}
|
||||||
|
|
||||||
withDates
|
withDates
|
||||||
:: (MonadFinance m, MonadInsertError m)
|
:: (MonadFinance m, MonadAppError m)
|
||||||
=> DaySpan
|
=> DaySpan
|
||||||
-> DatePat
|
-> DatePat
|
||||||
-> (Day -> m a)
|
-> (Day -> m a)
|
||||||
|
@ -1036,8 +1045,8 @@ sumM f = mapAccumM (\s -> fmap (first (+ s)) . f) 0
|
||||||
mapAccumM :: (Monad m) => (s -> a -> m (s, b)) -> s -> [a] -> m (s, [b])
|
mapAccumM :: (Monad m) => (s -> a -> m (s, b)) -> s -> [a] -> m (s, [b])
|
||||||
mapAccumM f s = foldM (\(s', ys) -> fmap (second (: ys)) . f s') (s, [])
|
mapAccumM f s = foldM (\(s', ys) -> fmap (second (: ys)) . f s') (s, [])
|
||||||
|
|
||||||
realFracToDecimal' :: (Integral i, RealFrac r) => Precision -> r -> DecimalRaw i
|
realFracToDecimalP :: (Integral i, RealFrac r) => Precision -> r -> DecimalRaw i
|
||||||
realFracToDecimal' p = realFracToDecimal (unPrecision p)
|
realFracToDecimalP p = realFracToDecimal (unPrecision p)
|
||||||
|
|
||||||
roundToP :: Integral i => Precision -> DecimalRaw i -> DecimalRaw i
|
roundToP :: Integral i => Precision -> DecimalRaw i -> DecimalRaw i
|
||||||
roundToP p = roundTo (unPrecision p)
|
roundToP p = roundTo (unPrecision p)
|
||||||
|
|
Loading…
Reference in New Issue