{-# LANGUAGE OverloadedStrings #-} module Model.Payment ( PaymentId , Payment(..) , find , list , listMonthly , create , createMany , editOwn , deleteOwn , modifiedDuring ) where import Data.Int (Int64) import Data.Maybe (listToMaybe) import Data.Text (Text) import Data.Time (UTCTime) import Data.Time.Calendar (Day) import Data.Time.Clock (getCurrentTime) import Database.SQLite.Simple (Only(Only), FromRow(fromRow), ToRow) import Database.SQLite.Simple.ToField (ToField(toField)) import Prelude hiding (id) import qualified Database.SQLite.Simple as SQLite import Model.Frequency import Model.Query (Query(Query)) import Model.User (UserId) import Resource (Resource, resourceCreatedAt, resourceEditedAt, resourceDeletedAt) type PaymentId = Int64 data Payment = Payment { id :: PaymentId , userId :: UserId , name :: Text , cost :: Int , date :: Day , frequency :: Frequency , createdAt :: UTCTime , editedAt :: Maybe UTCTime , deletedAt :: Maybe UTCTime } deriving Show instance Resource Payment where resourceCreatedAt = createdAt resourceEditedAt = editedAt resourceDeletedAt = deletedAt instance FromRow Payment where fromRow = Payment <$> SQLite.field <*> SQLite.field <*> SQLite.field <*> SQLite.field <*> SQLite.field <*> SQLite.field <*> SQLite.field <*> SQLite.field <*> SQLite.field instance ToRow Payment where toRow p = [ toField (userId p) , toField (name p) , toField (cost p) , toField (date p) , toField (frequency p) , toField (createdAt p) , toField (createdAt p) ] find :: PaymentId -> Query (Maybe Payment) find paymentId = Query (\conn -> listToMaybe <$> SQLite.query conn "SELECT * FROM payment WHERE id = ?" (Only paymentId) ) list :: Query [Payment] list = Query (\conn -> SQLite.query_ conn "SELECT * FROM payment WHERE deleted_at IS NULL" ) listMonthly :: Query [Payment] listMonthly = Query (\conn -> SQLite.query conn "SELECT * FROM payment WHERE deleted_at IS NULL AND frequency = ? ORDER BY name DESC" (Only Monthly) ) create :: UserId -> Text -> Int -> Day -> Frequency -> Query PaymentId create paymentUserId paymentName paymentCost paymentDate paymentFrequency = Query (\conn -> do now <- getCurrentTime SQLite.execute conn "INSERT INTO payment (user_id, name, cost, date, frequency, created_at) VALUES (?, ?, ?, ?, ?, ?)" (paymentUserId, paymentName, paymentCost, paymentDate, paymentFrequency, now) SQLite.lastInsertRowId conn ) createMany :: [Payment] -> Query () createMany payments = Query (\conn -> SQLite.executeMany conn "INSERT INTO payment (user_id, name, cost, date, frequency, created_at) VALUES (?, ?, ?, ?, ?, ?)" payments ) editOwn :: UserId -> PaymentId -> Text -> Int -> Day -> Frequency -> Query Bool editOwn paymentUserId paymentId paymentName paymentCost paymentDate paymentFrequency = Query (\conn -> do mbPayment <- listToMaybe <$> SQLite.query conn "SELECT * FROM payment WHERE id = ?" (Only paymentId) case mbPayment of Just payment -> if userId payment == paymentUserId then do now <- getCurrentTime SQLite.execute conn "UPDATE payment SET edited_at = ?, name = ?, cost = ?, date = ?, frequency = ? WHERE id = ?" (now, paymentName, paymentCost, paymentDate, paymentFrequency, paymentId) return True else return False Nothing -> return False ) deleteOwn :: UserId -> PaymentId -> Query Bool deleteOwn paymentUserId paymentId = Query (\conn -> do mbPayment <- listToMaybe <$> SQLite.query conn "SELECT * FROM payment WHERE id = ?" (Only paymentId) case mbPayment of Just payment -> if userId payment == paymentUserId then do now <- getCurrentTime SQLite.execute conn "UPDATE payment SET deleted_at = ? WHERE id = ?" (now, paymentId) return True else return False Nothing -> return False ) modifiedDuring :: UTCTime -> UTCTime -> Query [Payment] modifiedDuring start end = Query (\conn -> SQLite.query conn "SELECT * FROM payment WHERE (created_at >= ? AND created_at <= ?) OR (edited_at >= ? AND edited_at <= ?) OR (deleted_at >= ? AND deleted_at <= ?)" (start, end, start, end, start, end) )