packages feed

snelstart-import-1.0.0: src/SnelstartImport/ING.hs

{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE QuasiQuotes #-}

-- | We target the ING bank as the output format.
--   "snelstart" understands this csv format
module SnelstartImport.ING
  ( ING(..),
    toCode,
    writeCsv,
    MutatieSoort (..),
    BijAf(..)
  )
where

import SnelstartImport.Currency
import Data.Text
import Data.Text.Encoding
import Data.Csv
import Data.Time
import GHC.Generics
import qualified Data.Vector as Vector
import NeatInterpolation(text)
import qualified Data.ByteString.Lazy as LBS

data MutatieSoort = Overschijving | Diversen | Incasso
  deriving Show

mutatieSoortToFIeld :: MutatieSoort -> Text
mutatieSoortToFIeld = \case
  Overschijving -> "Overschrijving"
  Diversen -> "Diversen"
  Incasso -> "Incasso"

toCode :: MutatieSoort -> Text
toCode = \case
  Overschijving -> "OV"
  Diversen -> "DV"
  Incasso -> "IC"

data BijAf = Bij | Af
  deriving Show

bijAfToField :: BijAf -> Text
bijAfToField = \case
  Bij -> "Bij"
  Af -> "Af"


data ING = ING {
  datum :: UTCTime,
  naamBescrhijving :: Text,
  rekening :: Text,
  tegenRekening :: Text,
  mutatieSoort ::  MutatieSoort, -- eg ook voor code
  bijAf :: BijAf,
  bedragEur :: Currency ,
  mededeling :: Text
  }
  deriving stock (Generic, Show)

instance ToRecord ING where
  toRecord (ING{..}) =
    Vector.fromList
      [ toField $ formatTime defaultTimeLocale "%Y%m%d" datum
      , toField naamBescrhijving
      , toField rekening
      , toField tegenRekening
      , toField $ toCode mutatieSoort
      , toField $ bijAfToField bijAf
      , toField $ replaceDotWithComma <$>  Prelude.show bedragEur
      , toField $ mutatieSoortToFIeld mutatieSoort
      , toField mededeling
      ]

data TransactionType = MastercardPayment | OutgoingTransfer | Income | N26Referal | DirectDebit
  deriving stock (Show, Eq)

replaceDotWithComma :: Char -> Char
replaceDotWithComma '.' = ','
replaceDotWithComma other = other

instance FromField TransactionType where
  parseField field = case decodeUtf8 field of
    "MasterCard Payment" -> pure MastercardPayment
    "Outgoing Transfer" -> pure OutgoingTransfer
    "Income" -> pure Income
    "N26 Referral" -> pure N26Referal
    "Direct Debit" -> pure DirectDebit
    other -> fail $ "TransactionType unkown" <> unpack other

newtype Date = Date UTCTime
  deriving newtype (Show, Eq, Read)

instance FromField Date where
  parseField field =
    fmap Date $ parseTimeM True defaultTimeLocale "%Y-%m-%d" $ unpack $ decodeUtf8 field


writeCsv :: [ING] -> LBS.ByteString
writeCsv lines' = LBS.fromStrict header' <> data'
  where
        data' :: LBS.ByteString
        data' = encodeWith opts lines'
        header' = encodeUtf8 $ [text|"Datum","Naam / Omschrijving","Rekening","Tegenrekening","Code","Af Bij","Bedrag (EUR)","Mutatiesoort","Mededelingen"|] <> "\n"

opts :: EncodeOptions
opts = defaultEncodeOptions { encQuoting = QuoteAll}