hledger/bin/hledger-check-dates.hs

90 lines
2.8 KiB
Haskell
Raw Normal View History

#!/usr/bin/env stack
{- stack runghc --verbosity info
--package hledger-lib
--package hledger
--package here
-}
{-# LANGUAGE QuasiQuotes #-}
import Data.String.Here
import Hledger
import Hledger.Cli
import Text.Printf
------------------------------------------------------------------------------
cmdmode =
let m = defAddonCommandMode "check-dates"
in m {
modeHelp = [here|
Check that transactions' dates are monotonically increasing.
With --date2, checks secondary dates instead.
2017-01-08 21:24:33 +03:00
With --strict, dates must also be unique.
With a query, only matched transactions' dates are checked.
2017-01-08 21:24:33 +03:00
Reads the default journal file, or another specified with -f.
|]
,modeHelpSuffix=lines [here|
|]
,modeGroupFlags = (modeGroupFlags m) {
groupUnnamed = [
flagNone ["strict"] (\opts -> setboolopt "strict" opts) "makes date comparing strict"
]
}
}
------------------------------------------------------------------------------
2014-11-03 09:37:57 +03:00
main :: IO ()
main = do
opts <- getHledgerCliOpts cmdmode
withJournalDo opts $
\CliOpts{rawopts_=opts,reportopts_=ropts} j -> do
d <- getCurrentDay
let ropts_ = ropts{accountlistmode_=ALFlat}
let q = queryFromOpts d ropts_
let ts = filter (q `matchesTransaction`) $
jtxns $ journalSelectingAmountFromOpts ropts j
let strict = boolopt "strict" opts
let date = transactionDateFn ropts
let compare a b =
if strict
then date a < date b
else date a <= date b
case checkTransactions compare ts of
FoldAcc{fa_previous=Nothing} -> putStrLn "ok (empty journal)"
FoldAcc{fa_error=Nothing} -> putStrLn "ok"
FoldAcc{fa_error=Just error, fa_previous=Just previous} ->
putStrLn $ printf ("ERROR: transaction out of%s date order"
++ "\nPrevious date: %s"
++ "\nDate: %s"
++ "\nLocation: %s"
++ "\nTransaction:\n\n%s")
(if strict then " STRICT" else "")
(show $ date previous)
(show $ date error)
(show $ tsourcepos error)
(showTransactionUnelided error)
data FoldAcc a b = FoldAcc
{ fa_error :: Maybe a
, fa_previous :: Maybe b
}
foldWhile :: (a -> FoldAcc a b -> FoldAcc a b) -> FoldAcc a b -> [a] -> FoldAcc a b
foldWhile _ acc [] = acc
foldWhile fold acc (a:as) =
case fold a acc of
acc@FoldAcc{fa_error=Just _} -> acc
acc -> foldWhile fold acc as
checkTransactions :: (Transaction -> Transaction -> Bool)
-> [Transaction] -> FoldAcc Transaction Transaction
checkTransactions compare ts =
foldWhile fold FoldAcc{fa_error=Nothing, fa_previous=Nothing} ts
where
fold current acc@FoldAcc{fa_previous=Nothing} = acc{fa_previous=Just current}
fold current acc@FoldAcc{fa_previous=Just previous} =
if compare previous current
then acc{fa_previous=Just current}
else acc{fa_error=Just current}