WIP show useful errors for insertions
This commit is contained in:
parent
7ad754bead
commit
b94fd4b667
|
@ -128,17 +128,16 @@ insertBudget Budget {income = is, expenses = es} = do
|
||||||
|
|
||||||
-- TODO this hashes twice (not that it really matters)
|
-- TODO this hashes twice (not that it really matters)
|
||||||
whenHash
|
whenHash
|
||||||
:: Hashable a
|
:: (Hashable a, MonadUnliftIO m)
|
||||||
=> MonadUnliftIO m
|
|
||||||
=> ConfigType
|
=> ConfigType
|
||||||
-> a
|
-> a
|
||||||
-> (Key CommitR -> MappingT m ())
|
-> b
|
||||||
-> MappingT m ()
|
-> (Key CommitR -> MappingT m b)
|
||||||
whenHash t o f = do
|
-> MappingT m b
|
||||||
|
whenHash t o def f = do
|
||||||
let h = hash o
|
let h = hash o
|
||||||
hs <- asks kmNewCommits
|
hs <- asks kmNewCommits
|
||||||
when (h `elem` hs) $ do
|
if h `elem` hs then f =<< lift (insert $ CommitR h t) else return def
|
||||||
f =<< lift (insert $ CommitR h t)
|
|
||||||
|
|
||||||
insertIncome :: MonadUnliftIO m => Income -> MappingT m ()
|
insertIncome :: MonadUnliftIO m => Income -> MappingT m ()
|
||||||
insertIncome
|
insertIncome
|
||||||
|
@ -148,7 +147,7 @@ insertIncome
|
||||||
, incAccount = from
|
, incAccount = from
|
||||||
, incTaxes = ts
|
, incTaxes = ts
|
||||||
} =
|
} =
|
||||||
whenHash CTIncome i $ \c -> do
|
whenHash CTIncome i () $ \c -> do
|
||||||
case balanceIncome i of
|
case balanceIncome i of
|
||||||
Left m -> liftIO $ print m
|
Left m -> liftIO $ print m
|
||||||
Right as -> do
|
Right as -> do
|
||||||
|
@ -244,7 +243,7 @@ insertExpense
|
||||||
, expBucket = buc
|
, expBucket = buc
|
||||||
, expAmounts = as
|
, expAmounts = as
|
||||||
} = do
|
} = do
|
||||||
whenHash CTExpense e $ \c -> do
|
whenHash CTExpense e () $ \c -> do
|
||||||
ts <- concat <$> mapM (timeAmountToTx from to cur) as
|
ts <- concat <$> mapM (timeAmountToTx from to cur) as
|
||||||
lift $ mapM_ (insertTxBucket (Just buc) c) ts
|
lift $ mapM_ (insertTxBucket (Just buc) c) ts
|
||||||
|
|
||||||
|
@ -276,10 +275,12 @@ timeAmountToTx
|
||||||
-- statements
|
-- statements
|
||||||
|
|
||||||
insertStatements :: MonadUnliftIO m => Config -> MappingT m ()
|
insertStatements :: MonadUnliftIO m => Config -> MappingT m ()
|
||||||
insertStatements = mapM_ insertStatement . statements
|
insertStatements conf = do
|
||||||
|
es <- catMaybes <$> mapM insertStatement (statements conf)
|
||||||
|
unless (null es) $ throwIO $ InsertException es
|
||||||
|
|
||||||
insertStatement :: MonadUnliftIO m => Statement -> MappingT m ()
|
insertStatement :: MonadUnliftIO m => Statement -> MappingT m (Maybe InsertError)
|
||||||
insertStatement (StmtManual m) = insertManual m
|
insertStatement (StmtManual m) = insertManual m >> return Nothing
|
||||||
insertStatement (StmtImport i) = insertImport i
|
insertStatement (StmtImport i) = insertImport i
|
||||||
|
|
||||||
insertManual :: MonadUnliftIO m => Manual -> MappingT m ()
|
insertManual :: MonadUnliftIO m => Manual -> MappingT m ()
|
||||||
|
@ -292,24 +293,25 @@ insertManual
|
||||||
, manualCurrency = u
|
, manualCurrency = u
|
||||||
, manualDesc = e
|
, manualDesc = e
|
||||||
} = do
|
} = do
|
||||||
whenHash CTManual m $ \c -> do
|
whenHash CTManual m () $ \c -> do
|
||||||
bounds <- (liftIO . resolveBounds) =<< asks kmStatementInterval
|
bounds <- (liftIO . resolveBounds) =<< asks kmStatementInterval
|
||||||
ts <- mapM tx $ expandDatePat bounds dp
|
ts <- mapM tx $ expandDatePat bounds dp
|
||||||
lift $ mapM_ (insertTx c) ts
|
lift $ mapM_ (insertTx c) ts
|
||||||
where
|
where
|
||||||
tx day = txPair day from to u (dec2Rat v) e
|
tx day = txPair day from to u (dec2Rat v) e
|
||||||
|
|
||||||
insertImport :: MonadUnliftIO m => Import -> MappingT m ()
|
insertImport :: MonadUnliftIO m => Import -> MappingT m (Maybe InsertError)
|
||||||
insertImport i = whenHash CTImport i $ \c -> do
|
insertImport i = whenHash CTImport i Nothing $ \c -> do
|
||||||
bounds <- asks kmStatementInterval
|
bounds <- asks kmStatementInterval
|
||||||
res <- readImport i
|
res <- readImport i
|
||||||
-- TODO this isn't efficient, the whole file will be read and maybe no
|
-- TODO this isn't efficient, the whole file will be read and maybe no
|
||||||
-- transactions will be desired
|
-- transactions will be desired
|
||||||
case res of
|
case res of
|
||||||
StatementPass bs -> do
|
Right bs -> do
|
||||||
rs <- mapM resolveTx $ filter (inMaybeBounds bounds . txDate) bs
|
rs <- mapM resolveTx $ filter (inMaybeBounds bounds . txDate) bs
|
||||||
lift $ mapM_ (insertTx c) rs
|
lift $ mapM_ (insertTx c) rs
|
||||||
StatementFail _ -> throwIO MatchException
|
return Nothing
|
||||||
|
Left e -> return $ Just e
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- low-level transaction stuff
|
-- low-level transaction stuff
|
||||||
|
|
|
@ -23,12 +23,10 @@ import qualified RIO.Vector as V
|
||||||
|
|
||||||
-- TODO this probably won't scale well (pipes?)
|
-- TODO this probably won't scale well (pipes?)
|
||||||
|
|
||||||
readImport :: MonadUnliftIO m => Import -> MappingT m StatementRes
|
readImport :: MonadUnliftIO m => Import -> MappingT m (EitherErr [BalTx])
|
||||||
readImport Import {..} = do
|
readImport Import {..} =
|
||||||
rs <- L.sort . concat <$> mapM (readImport_ impSkipLines impDelim impTxOpts) impPaths
|
matchRecords impMatches . L.sort . concat
|
||||||
-- TODO show more useful information here (eg which file emitted this
|
<$> mapM (readImport_ impSkipLines impDelim impTxOpts) impPaths
|
||||||
-- error and possibly where)
|
|
||||||
return $ matchRecords impMatches rs
|
|
||||||
|
|
||||||
readImport_
|
readImport_
|
||||||
:: MonadUnliftIO m
|
:: MonadUnliftIO m
|
||||||
|
@ -40,7 +38,7 @@ readImport_
|
||||||
readImport_ n delim tns p = do
|
readImport_ n delim tns p = do
|
||||||
dir <- asks kmConfigDir
|
dir <- asks kmConfigDir
|
||||||
bs <- liftIO $ BL.readFile $ dir </> p
|
bs <- liftIO $ BL.readFile $ dir </> p
|
||||||
case decodeByNameWithP (parseTxRecord tns) opts $ skip bs of
|
case decodeByNameWithP (parseTxRecord p tns) opts $ skip bs of
|
||||||
Left m -> liftIO $ putStrLn m >> return []
|
Left m -> liftIO $ putStrLn m >> return []
|
||||||
Right (_, v) -> return $ catMaybes $ V.toList v
|
Right (_, v) -> return $ catMaybes $ V.toList v
|
||||||
where
|
where
|
||||||
|
@ -49,8 +47,8 @@ readImport_ n delim tns p = do
|
||||||
|
|
||||||
-- TODO handle this better, this maybe thing is a hack to skip lines with
|
-- TODO handle this better, this maybe thing is a hack to skip lines with
|
||||||
-- blank dates but will likely want to make this more flexible
|
-- blank dates but will likely want to make this more flexible
|
||||||
parseTxRecord :: TxOpts -> NamedRecord -> Parser (Maybe TxRecord)
|
parseTxRecord :: FilePath -> TxOpts -> NamedRecord -> Parser (Maybe TxRecord)
|
||||||
parseTxRecord TxOpts {..} r = do
|
parseTxRecord p TxOpts {..} r = do
|
||||||
d <- r .: T.encodeUtf8 toDate
|
d <- r .: T.encodeUtf8 toDate
|
||||||
if d == ""
|
if d == ""
|
||||||
then return Nothing
|
then return Nothing
|
||||||
|
@ -59,17 +57,16 @@ parseTxRecord TxOpts {..} r = do
|
||||||
e <- r .: T.encodeUtf8 toDesc
|
e <- r .: T.encodeUtf8 toDesc
|
||||||
os <- M.fromList <$> mapM (\n -> (n,) <$> r .: T.encodeUtf8 n) toOther
|
os <- M.fromList <$> mapM (\n -> (n,) <$> r .: T.encodeUtf8 n) toOther
|
||||||
d' <- parseTimeM True defaultTimeLocale (T.unpack toDateFmt) d
|
d' <- parseTimeM True defaultTimeLocale (T.unpack toDateFmt) d
|
||||||
return $ Just $ TxRecord d' a e os
|
return $ Just $ TxRecord d' a e os p
|
||||||
|
|
||||||
matchRecords :: [Match] -> [TxRecord] -> StatementRes
|
matchRecords :: [Match] -> [TxRecord] -> EitherErr [BalTx]
|
||||||
matchRecords ms rs = case matchAll (matchPriorities ms) rs of
|
matchRecords ms rs = do
|
||||||
Left e -> StatementFail $ StatementErrors [] [] [e]
|
(matched, unmatched, notfound) <- matchAll (matchPriorities ms) rs
|
||||||
Right (matched, unmatched, notfound) ->
|
|
||||||
-- TODO record number of times each match hits for debugging
|
-- TODO record number of times each match hits for debugging
|
||||||
let (errors, matched_) = partitionEithers $ balanceTx <$> matched
|
matched_ <- mapM balanceTx matched
|
||||||
in case (matched_, unmatched, notfound, errors) of
|
case (matched_, unmatched, notfound) of
|
||||||
(xs, [], [], []) -> StatementPass xs
|
(xs, [], []) -> Right xs
|
||||||
(_, us, ns, es) -> StatementFail $ StatementErrors us ns es
|
(_, us, ns) -> Left $ StatementError us ns
|
||||||
|
|
||||||
matchPriorities :: [Match] -> [MatchGroup]
|
matchPriorities :: [Match] -> [MatchGroup]
|
||||||
matchPriorities =
|
matchPriorities =
|
||||||
|
@ -117,7 +114,7 @@ zipperSlice f x = go
|
||||||
EQ -> goEq $ Unzipped bs (a : cs) as
|
EQ -> goEq $ Unzipped bs (a : cs) as
|
||||||
LT -> z
|
LT -> z
|
||||||
|
|
||||||
zipperMatch :: Unzipped Match -> TxRecord -> PureErr (Zipped Match, MatchRes RawTx)
|
zipperMatch :: Unzipped Match -> TxRecord -> EitherErr (Zipped Match, MatchRes RawTx)
|
||||||
zipperMatch (Unzipped bs cs as) x = go [] cs
|
zipperMatch (Unzipped bs cs as) x = go [] cs
|
||||||
where
|
where
|
||||||
go _ [] = Right (Zipped bs $ cs ++ as, MatchFail)
|
go _ [] = Right (Zipped bs $ cs ++ as, MatchFail)
|
||||||
|
@ -130,7 +127,7 @@ zipperMatch (Unzipped bs cs as) x = go [] cs
|
||||||
ms' = maybe ms (: ms) (matchDec m)
|
ms' = maybe ms (: ms) (matchDec m)
|
||||||
in Right (Zipped bs $ ps ++ ms' ++ as, skipOrPass)
|
in Right (Zipped bs $ ps ++ ms' ++ as, skipOrPass)
|
||||||
|
|
||||||
zipperMatch' :: Zipped Match -> TxRecord -> PureErr (Zipped Match, MatchRes RawTx)
|
zipperMatch' :: Zipped Match -> TxRecord -> EitherErr (Zipped Match, MatchRes RawTx)
|
||||||
zipperMatch' z x = go z
|
zipperMatch' z x = go z
|
||||||
where
|
where
|
||||||
go (Zipped bs (a : as)) = do
|
go (Zipped bs (a : as)) = do
|
||||||
|
@ -146,7 +143,7 @@ matchDec m@Match {mTimes = t} =
|
||||||
where
|
where
|
||||||
t' = fmap pred t
|
t' = fmap pred t
|
||||||
|
|
||||||
matchAll :: [MatchGroup] -> [TxRecord] -> PureErr ([RawTx], [TxRecord], [Match])
|
matchAll :: [MatchGroup] -> [TxRecord] -> EitherErr ([RawTx], [TxRecord], [Match])
|
||||||
matchAll = go ([], [])
|
matchAll = go ([], [])
|
||||||
where
|
where
|
||||||
go (matched, unused) gs rs = case (gs, rs) of
|
go (matched, unused) gs rs = case (gs, rs) of
|
||||||
|
@ -156,13 +153,13 @@ matchAll = go ([], [])
|
||||||
(ts, unmatched, us) <- matchGroup g rs
|
(ts, unmatched, us) <- matchGroup g rs
|
||||||
go (ts ++ matched, us ++ unused) gs' unmatched
|
go (ts ++ matched, us ++ unused) gs' unmatched
|
||||||
|
|
||||||
matchGroup :: MatchGroup -> [TxRecord] -> PureErr ([RawTx], [TxRecord], [Match])
|
matchGroup :: MatchGroup -> [TxRecord] -> EitherErr ([RawTx], [TxRecord], [Match])
|
||||||
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
|
||||||
return (md ++ mn, unmatched, filter ((/= Nothing) . mTimes) $ ud ++ un)
|
return (md ++ mn, unmatched, filter ((/= Nothing) . mTimes) $ ud ++ un)
|
||||||
|
|
||||||
matchDates :: [Match] -> [TxRecord] -> PureErr ([RawTx], [TxRecord], [Match])
|
matchDates :: [Match] -> [TxRecord] -> EitherErr ([RawTx], [TxRecord], [Match])
|
||||||
matchDates ms = go ([], [], initZipper ms)
|
matchDates ms = go ([], [], initZipper ms)
|
||||||
where
|
where
|
||||||
go (matched, unmatched, z) [] =
|
go (matched, unmatched, z) [] =
|
||||||
|
@ -182,7 +179,7 @@ matchDates ms = go ([], [], initZipper ms)
|
||||||
go (m, u, z') rs
|
go (m, u, z') rs
|
||||||
findDate m r = maybe EQ (`compareDate` trDate r) $ mDate m
|
findDate m r = maybe EQ (`compareDate` trDate r) $ mDate m
|
||||||
|
|
||||||
matchNonDates :: [Match] -> [TxRecord] -> PureErr ([RawTx], [TxRecord], [Match])
|
matchNonDates :: [Match] -> [TxRecord] -> EitherErr ([RawTx], [TxRecord], [Match])
|
||||||
matchNonDates ms = go ([], [], initZipper ms)
|
matchNonDates ms = go ([], [], initZipper ms)
|
||||||
where
|
where
|
||||||
go (matched, unmatched, z) [] =
|
go (matched, unmatched, z) [] =
|
||||||
|
@ -199,12 +196,12 @@ 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
|
||||||
|
|
||||||
balanceTx :: RawTx -> PureErr BalTx
|
balanceTx :: RawTx -> EitherErr BalTx
|
||||||
balanceTx t@Tx {txSplits = ss} = do
|
balanceTx t@Tx {txSplits = ss} = do
|
||||||
bs <- balanceSplits ss
|
bs <- balanceSplits ss
|
||||||
return $ t {txSplits = bs}
|
return $ t {txSplits = bs}
|
||||||
|
|
||||||
balanceSplits :: [RawSplit] -> PureErr [BalSplit]
|
balanceSplits :: [RawSplit] -> EitherErr [BalSplit]
|
||||||
balanceSplits ss =
|
balanceSplits ss =
|
||||||
fmap concat
|
fmap concat
|
||||||
<$> mapM (uncurry bal)
|
<$> mapM (uncurry bal)
|
||||||
|
@ -214,11 +211,11 @@ balanceSplits ss =
|
||||||
hasValue s@(Split {sValue = Just v}) = Right s {sValue = v}
|
hasValue s@(Split {sValue = Just v}) = Right s {sValue = v}
|
||||||
hasValue s = Left s
|
hasValue s = Left s
|
||||||
bal cur rss
|
bal cur rss
|
||||||
| length rss < 2 = Left $ T.append "Need at least two splits to balance: " cur
|
| length rss < 2 = Left $ BalanceError TooFewSplits cur
|
||||||
| otherwise = case partitionEithers $ fmap hasValue rss of
|
| otherwise = case partitionEithers $ fmap hasValue rss of
|
||||||
([noVal], val) -> Right $ noVal {sValue = foldr (\s x -> x - sValue s) 0 val} : val
|
([noVal], val) -> Right $ noVal {sValue = foldr (\s x -> x - sValue s) 0 val} : val
|
||||||
([], val) -> Right val
|
([], val) -> Right val
|
||||||
_ -> Left $ T.append "Exactly one split must be blank: " cur
|
_ -> Left $ BalanceError NotOneBlank cur
|
||||||
|
|
||||||
groupByKey :: Ord k => [(k, v)] -> [(k, [v])]
|
groupByKey :: Ord k => [(k, v)] -> [(k, [v])]
|
||||||
groupByKey = M.toList . M.fromListWith (++) . fmap (second (: []))
|
groupByKey = M.toList . M.fromListWith (++) . fmap (second (: []))
|
||||||
|
|
|
@ -458,6 +458,7 @@ data TxRecord = TxRecord
|
||||||
, trAmount :: !Rational
|
, trAmount :: !Rational
|
||||||
, trDesc :: !T.Text
|
, trDesc :: !T.Text
|
||||||
, trOther :: M.Map T.Text T.Text
|
, trOther :: M.Map T.Text T.Text
|
||||||
|
, trFile :: FilePath
|
||||||
}
|
}
|
||||||
deriving (Show, Eq, Ord)
|
deriving (Show, Eq, Ord)
|
||||||
|
|
||||||
|
@ -503,19 +504,20 @@ type RawTx = Tx RawSplit
|
||||||
|
|
||||||
type BalTx = Tx BalSplit
|
type BalTx = Tx BalSplit
|
||||||
|
|
||||||
type PureErr a = Either T.Text a
|
|
||||||
|
|
||||||
data MatchRes a = MatchPass a | MatchFail | MatchSkip
|
data MatchRes a = MatchPass a | MatchFail | MatchSkip
|
||||||
|
|
||||||
data InsertException = MatchException | RegexException deriving (Show)
|
data BalanceType = TooFewSplits | NotOneBlank deriving (Show)
|
||||||
|
|
||||||
-- TODO retain file information here for clearer printing purposes
|
data InsertError
|
||||||
data StatementErrors = StatementErrors
|
= RegexError T.Text
|
||||||
{ seUnmatched :: [TxRecord]
|
| ConversionError T.Text
|
||||||
, seNotFound :: [Match]
|
| LookupError T.Text
|
||||||
, seErrors :: [T.Text]
|
| BalanceError BalanceType CurID
|
||||||
}
|
| StatementError [TxRecord] [Match]
|
||||||
|
deriving (Show)
|
||||||
|
|
||||||
data StatementRes = StatementPass [BalTx] | StatementFail StatementErrors
|
newtype InsertException = InsertException [InsertError] deriving (Show)
|
||||||
|
|
||||||
instance Exception InsertException
|
instance Exception InsertException
|
||||||
|
|
||||||
|
type EitherErr = Either InsertError
|
||||||
|
|
|
@ -116,7 +116,7 @@ lookupErr what k m = case M.lookup k m of
|
||||||
Just x -> x
|
Just x -> x
|
||||||
_ -> errorT $ T.concat [what, " does not exist: ", T.pack $ show k]
|
_ -> errorT $ T.concat [what, " does not exist: ", T.pack $ show k]
|
||||||
|
|
||||||
matches :: Match -> TxRecord -> PureErr (MatchRes RawTx)
|
matches :: Match -> TxRecord -> EitherErr (MatchRes RawTx)
|
||||||
matches Match {..} r@TxRecord {..} = do
|
matches Match {..} r@TxRecord {..} = do
|
||||||
let date = checkMaybe (`dateMatches` trDate) mDate
|
let date = checkMaybe (`dateMatches` trDate) mDate
|
||||||
let val = valMatches mVal trAmount
|
let val = valMatches mVal trAmount
|
||||||
|
@ -129,19 +129,17 @@ matches Match {..} r@TxRecord {..} = do
|
||||||
where
|
where
|
||||||
eval (ToTx cur a ss) = toTx cur a ss r
|
eval (ToTx cur a ss) = toTx cur a ss r
|
||||||
|
|
||||||
matchMaybe :: RegexContext Regex query b => query -> T.Text -> PureErr b
|
matchMaybe :: RegexContext Regex query b => query -> T.Text -> EitherErr b
|
||||||
matchMaybe q re = first (const msg) $ pureTry $ q =~ re
|
matchMaybe q re = first (const $ RegexError re) $ pureTry $ q =~ re
|
||||||
where
|
|
||||||
msg = T.concat ["Could not make regexp from pattern: '", re, "'"]
|
|
||||||
|
|
||||||
fieldMatches :: M.Map T.Text T.Text -> MatchOther -> PureErr Bool
|
fieldMatches :: M.Map T.Text T.Text -> MatchOther -> EitherErr Bool
|
||||||
fieldMatches dict m = case m of
|
fieldMatches dict m = case m of
|
||||||
Val (Field n mv) -> valMatches mv <$> (readRationalMsg =<< lookup_ n)
|
Val (Field n mv) -> valMatches mv <$> (readRationalMsg =<< lookup_ n)
|
||||||
Desc (Field n md) -> (`matchMaybe` md) =<< lookup_ n
|
Desc (Field n md) -> (`matchMaybe` md) =<< lookup_ n
|
||||||
where
|
where
|
||||||
lookup_ n = case M.lookup n dict of
|
lookup_ n = case M.lookup n dict of
|
||||||
Just r -> Right r
|
Just r -> Right r
|
||||||
Nothing -> Left $ T.append "Could not find field: " n
|
Nothing -> Left $ LookupError n
|
||||||
|
|
||||||
checkMaybe :: (a -> Bool) -> Maybe a -> Bool
|
checkMaybe :: (a -> Bool) -> Maybe a -> Bool
|
||||||
checkMaybe = maybe True
|
checkMaybe = maybe True
|
||||||
|
@ -196,10 +194,8 @@ parseRational pat s = case ms of
|
||||||
k <- readSign sign
|
k <- readSign sign
|
||||||
return (k, w)
|
return (k, w)
|
||||||
|
|
||||||
readRationalMsg :: T.Text -> PureErr Rational
|
readRationalMsg :: T.Text -> EitherErr Rational
|
||||||
readRationalMsg t = maybe (Left msg) Right $ readRational t
|
readRationalMsg t = maybe (Left $ ConversionError t) Right $ readRational t
|
||||||
where
|
|
||||||
msg = T.append "Could not convert to rational number: " t
|
|
||||||
|
|
||||||
-- TODO don't use a partial function
|
-- TODO don't use a partial function
|
||||||
readRational :: MonadFail m => T.Text -> m Rational
|
readRational :: MonadFail m => T.Text -> m Rational
|
||||||
|
@ -279,3 +275,16 @@ dec2Rat D {..} =
|
||||||
|
|
||||||
acntPath2Text :: AcntPath -> T.Text
|
acntPath2Text :: AcntPath -> T.Text
|
||||||
acntPath2Text (AcntPath t cs) = T.intercalate "/" (atName t : cs)
|
acntPath2Text (AcntPath t cs) = T.intercalate "/" (atName t : cs)
|
||||||
|
|
||||||
|
showError :: InsertError -> [T.Text]
|
||||||
|
showError (StatementError ts ms) = (showTx <$> ts) ++ (showMatch <$> ms)
|
||||||
|
where
|
||||||
|
showTx = undefined
|
||||||
|
showMatch = undefined
|
||||||
|
showError other = (: []) $ case other of
|
||||||
|
(RegexError re) -> T.append "could not make regex from pattern: " re
|
||||||
|
(ConversionError x) -> T.append "Could not convert to rational number: " x
|
||||||
|
(LookupError f) -> T.append "Could not find field: " f
|
||||||
|
-- TODO these balance errors are useless, need more info on the tx being balanced
|
||||||
|
(BalanceError TooFewSplits cur) -> T.append "Need at least two splits to balance: " cur
|
||||||
|
(BalanceError NotOneBlank cur) -> T.append "Exactly one split must be blank: " cur
|
||||||
|
|
Loading…
Reference in New Issue