module Model.Payment exposing ( perPage , Payments , Payment , PaymentId , Frequency(..) , paymentsDecoder , paymentIdDecoder , find , edit , delete , totalPayments , punctual , monthly , groupAndSortByMonth , search , validateFrequency ) import Date exposing (..) import Date.Extra.Core exposing (monthToInt, intToMonth) import Json.Decode as Decode exposing (Decoder) import Json.Decode.Extra as Decode import List import Form.Validate as Validate exposing (Validation) import Model.Date exposing (dateDecoder) import Model.User exposing (UserId, userIdDecoder) import Utils.List as List import Utils.Search as Search perPage : Int perPage = 7 type alias Payments = List Payment type alias Payment = { id : PaymentId , name : String , cost : Int , date : Date , userId : UserId , frequency : Frequency } type alias PaymentId = Int type Frequency = Punctual | Monthly paymentsDecoder : Decoder Payments paymentsDecoder = Decode.list paymentDecoder paymentDecoder : Decoder Payment paymentDecoder = Decode.map6 Payment (Decode.field "id" paymentIdDecoder) (Decode.field "name" Decode.string) (Decode.field "cost" Decode.int) (Decode.field "date" dateDecoder) (Decode.field "userId" userIdDecoder) (Decode.field "frequency" frequencyDecoder) paymentIdDecoder : Decoder PaymentId paymentIdDecoder = Decode.int frequencyDecoder : Decoder Frequency frequencyDecoder = let frequencyResult input = case input of "Punctual" -> Ok Punctual "Monthly" -> Ok Monthly _ -> Err ("Could not deduce Punctual nor Monthly from " ++ input) in Decode.string |> Decode.andThen (Decode.fromResult << frequencyResult) find : PaymentId -> Payments -> Maybe Payment find paymentId payments = payments |> List.filter (\p -> p.id == paymentId) |> List.head edit : Payment -> Payments -> Payments edit payment payments = payment :: delete payment.id payments delete : PaymentId -> Payments -> Payments delete paymentId = List.filter (((/=) paymentId) << .id) totalPayments : (Payment -> Bool) -> UserId -> Payments -> Int totalPayments paymentFilter userId payments = payments |> List.filter (\payment -> paymentFilter payment && payment.userId == userId ) |> List.map .cost |> List.sum punctual : Payments -> Payments punctual = List.filter ((==) Punctual << .frequency) monthly : Payments -> Payments monthly = List.filter ((==) Monthly << .frequency) groupAndSortByMonth : Payments -> List ((Month, Int), Payments) groupAndSortByMonth payments = payments |> List.groupBy (\payment -> (Date.year payment.date, monthToInt << Date.month <| payment.date)) |> List.sortBy Tuple.first |> List.map (\((year, month), payments) -> ((intToMonth month, year), payments)) |> List.reverse search : String -> Frequency -> Payments -> Payments search name frequency payments = payments |> List.filter ((==) frequency << .frequency) |> paymentSort frequency |> List.filter (searchSuccess name) paymentSort : Frequency -> Payments -> Payments paymentSort frequency = case frequency of Punctual -> List.reverse << List.sortBy (Date.toTime << .date) Monthly -> List.sortBy (String.toLower << .name) searchSuccess : String -> Payment -> Bool searchSuccess search { name, cost } = let searchSuccessWord word = ( String.contains (Search.format word) (Search.format name) || String.contains word (toString cost) ) in List.all searchSuccessWord (String.words search) validateFrequency : Validation String Frequency validateFrequency = Validate.customValidation Validate.string (\str -> if str == toString Punctual then Ok Punctual else if str == toString Monthly then Ok Monthly else Err (Validate.customError "InvalidFrequency") )