module Persistence.Income ( count , list , listAll , listModifiedSince , create , edit , delete , definedForAll ) where import qualified Data.List as L import qualified Data.Maybe as Maybe import qualified Data.Text as T import Data.Time.Calendar (Day) import Data.Time.Clock (UTCTime) import Data.Time.Clock (getCurrentTime) import Database.SQLite.Simple (FromRow (fromRow), Only (Only)) import qualified Database.SQLite.Simple as SQLite import Prelude hiding (id, until) import Common.Model (Income (..), IncomeId, PaymentId, UserId) import Model.Query (Query (Query)) newtype Row = Row Income instance FromRow Row where fromRow = Row <$> (Income <$> SQLite.field <*> SQLite.field <*> SQLite.field <*> SQLite.field <*> SQLite.field <*> SQLite.field <*> SQLite.field) data CountRow = CountRow Int instance FromRow CountRow where fromRow = CountRow <$> SQLite.field count :: Query Int count = Query (\conn -> (Maybe.fromMaybe 0 . fmap (\(CountRow n) -> n) . Maybe.listToMaybe) <$> SQLite.query_ conn "SELECT COUNT(*) FROM income WHERE deleted_at IS NULL" ) list :: Int -> Int -> Query [Income] list page perPage = Query (\conn -> map (\(Row i) -> i) <$> SQLite.query conn "SELECT * FROM income WHERE deleted_at IS NULL ORDER BY date DESC LIMIT ? OFFSET ?" (perPage, (page - 1) * perPage) ) listAll :: Query [Income] listAll = Query (\conn -> map (\(Row i) -> i) <$> SQLite.query_ conn "SELECT * FROM income WHERE deleted_at IS NULL" ) listModifiedSince :: UTCTime -> Query [Income] listModifiedSince since = Query (\conn -> map (\(Row i) -> i) <$> SQLite.query conn (SQLite.Query . T.intercalate " " $ [ "SELECT *" , "FROM income" , "WHERE" , "created_at >= ?" , "OR edited_at >= ?" , "OR deleted_at >= ?" ]) (Only since) ) create :: UserId -> Day -> Int -> Query Income create userId date amount = Query (\conn -> do createdAt <- getCurrentTime SQLite.execute conn "INSERT INTO income (user_id, date, amount, created_at) VALUES (?, ?, ?, ?)" (userId, date, amount, createdAt) incomeId <- SQLite.lastInsertRowId conn return $ Income { _income_id = incomeId , _income_userId = userId , _income_date = date , _income_amount = amount , _income_createdAt = createdAt , _income_editedAt = Nothing , _income_deletedAt = Nothing } ) edit :: UserId -> IncomeId -> Day -> Int -> Query (Maybe Income) edit userId incomeId incomeDate incomeAmount = Query (\conn -> do mbIncome <- fmap (\(Row i) -> i) . Maybe.listToMaybe <$> SQLite.query conn "SELECT * FROM income WHERE id = ?" (Only incomeId) case mbIncome of Just income -> do currentTime <- getCurrentTime SQLite.execute conn "UPDATE income SET edited_at = ?, date = ?, amount = ? WHERE id = ? AND user_id = ?" (currentTime, incomeDate, incomeAmount, incomeId, userId) return . Just $ Income { _income_id = incomeId , _income_userId = userId , _income_date = incomeDate , _income_amount = incomeAmount , _income_createdAt = _income_createdAt income , _income_editedAt = Just currentTime , _income_deletedAt = Nothing } Nothing -> return Nothing ) delete :: UserId -> PaymentId -> Query () delete userId paymentId = Query (\conn -> SQLite.execute conn "UPDATE income SET deleted_at = datetime('now') WHERE id = ? AND user_id = ?" (paymentId, userId) ) data UserDayRow = UserDayRow (UserId, Day) instance FromRow UserDayRow where fromRow = do user <- SQLite.field day <- SQLite.field return $ UserDayRow (user, day) definedForAll :: [UserId] -> Query (Maybe Day) definedForAll users = Query (\conn -> (fromRows . fmap (\(UserDayRow (user, day)) -> (user, day))) <$> SQLite.query_ conn "SELECT user_id, MIN(date) FROM income WHERE deleted_at IS NULL GROUP BY user_id;" ) where fromRows rows = if L.sort users == L.sort (map fst rows) then Maybe.listToMaybe . L.sort . map snd $ rows else Nothing