module Persistence.Payment ( Payment(..) , find , listActive , listPunctual , listActiveMonthlyOrderedByName , create , createMany , edit , delete ) where import Data.Maybe (listToMaybe) import Data.Text (Text) import qualified Data.Text as T import Data.Time.Calendar (Day) import Data.Time.Clock (getCurrentTime) import Database.SQLite.Simple (FromRow (fromRow), Only (Only), ToRow) import qualified Database.SQLite.Simple as SQLite import Database.SQLite.Simple.ToField (ToField (toField)) import Prelude hiding (id) import Common.Model (Frequency (..), Payment (..), PaymentId, UserId) import Model.Query (Query (Query)) import Persistence.Frequency (FrequencyField (..)) newtype Row = Row Payment instance FromRow Row where fromRow = Row <$> (Payment <$> SQLite.field <*> SQLite.field <*> SQLite.field <*> SQLite.field <*> SQLite.field <*> (fmap (\(FrequencyField f) -> f) $ SQLite.field) <*> SQLite.field <*> SQLite.field <*> SQLite.field) newtype InsertRow = InsertRow Payment instance ToRow InsertRow where toRow (InsertRow p) = [ toField (_payment_user p) , toField (_payment_name p) , toField (_payment_cost p) , toField (_payment_date p) , toField (FrequencyField (_payment_frequency p)) , toField (_payment_createdAt p) ] find :: PaymentId -> Query (Maybe Payment) find paymentId = Query (\conn -> do fmap (\(Row p) -> p) . listToMaybe <$> SQLite.query conn "SELECT * FROM payment WHERE id = ?" (Only paymentId) ) listActive :: Query [Payment] listActive = Query (\conn -> do map (\(Row p) -> p) <$> SQLite.query_ conn "SELECT * FROM payment WHERE deleted_at IS NULL" ) listPunctual :: Query [Payment] listPunctual = Query (\conn -> do map (\(Row p) -> p) <$> SQLite.query conn (SQLite.Query "SELECT * FROM payment WHERE frequency = ?") (Only (FrequencyField Punctual)) ) listActiveMonthlyOrderedByName :: Query [Payment] listActiveMonthlyOrderedByName = Query (\conn -> do map (\(Row p) -> p) <$> SQLite.query conn (SQLite.Query $ T.intercalate " " [ "SELECT *" , "FROM payment" , "WHERE deleted_at IS NULL AND frequency = ?" , "ORDER BY name DESC" ]) (Only (FrequencyField Monthly)) ) create :: UserId -> Text -> Int -> Day -> Frequency -> Query Payment create userId name cost date frequency = Query (\conn -> do time <- getCurrentTime SQLite.execute conn (SQLite.Query $ T.intercalate " " [ "INSERT INTO payment (user_id, name, cost, date, frequency, created_at)" , "VALUES (?, ?, ?, ?, ?, ?)" ]) (userId, name, cost, date, FrequencyField frequency, time) paymentId <- SQLite.lastInsertRowId conn return $ Payment { _payment_id = paymentId , _payment_user = userId , _payment_name = name , _payment_cost = cost , _payment_date = date , _payment_frequency = frequency , _payment_createdAt = time , _payment_editedAt = Nothing , _payment_deletedAt = Nothing } ) createMany :: [Payment] -> Query () createMany payments = Query (\conn -> SQLite.executeMany conn (SQLite.Query $ T.intercalate "" [ "INSERT INTO payment (user_id, name, cost, date, frequency, created_at)" , "VALUES (?, ?, ?, ?, ?, ?)" ]) (map InsertRow payments) ) edit :: UserId -> PaymentId -> Text -> Int -> Day -> Frequency -> Query (Maybe (Payment, Payment)) edit userId paymentId name cost date frequency = Query (\conn -> do mbPayment <- fmap (\(Row p) -> p) . listToMaybe <$> SQLite.query conn "SELECT * FROM payment WHERE id = ? and user_id = ?" (paymentId, userId) case mbPayment of Just payment -> do now <- getCurrentTime SQLite.execute conn (SQLite.Query $ T.intercalate " " [ "UPDATE" , " payment" , "SET" , " edited_at = ?," , " name = ?," , " cost = ?," , " date = ?," , " frequency = ?" , "WHERE" , " id = ?" , " AND user_id = ?" ]) ( now , name , cost , date , FrequencyField frequency , paymentId , userId ) return . Just . (,) payment $ Payment { _payment_id = paymentId , _payment_user = userId , _payment_name = name , _payment_cost = cost , _payment_date = date , _payment_frequency = frequency , _payment_createdAt = _payment_createdAt payment , _payment_editedAt = Just now , _payment_deletedAt = Nothing } Nothing -> return Nothing ) delete :: UserId -> PaymentId -> Query () delete userId paymentId = Query (\conn -> SQLite.execute conn "UPDATE payment SET deleted_at = datetime('now') WHERE id = ? AND user_id = ?" (paymentId, userId) )