packages feed

snelstart-import 1.0.1 → 1.1.0

raw patch · 6 files changed

+62/−21 lines, 6 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ SnelstartImport.SepaDirectCoreScheme: SepaDirectCoreResults :: [SepaDirectCoreScheme] -> SepaGlobals -> SepaDirectCoreResults
+ SnelstartImport.SepaDirectCoreScheme: SepaGlobals :: UTCTime -> Text -> SepaGlobals
+ SnelstartImport.SepaDirectCoreScheme: [cdtrAcct] :: SepaGlobals -> Text
+ SnelstartImport.SepaDirectCoreScheme: [creDtTm] :: SepaGlobals -> UTCTime
+ SnelstartImport.SepaDirectCoreScheme: [rmtInf] :: SepaDirectCoreScheme -> Text
+ SnelstartImport.SepaDirectCoreScheme: [sdcrGlob] :: SepaDirectCoreResults -> SepaGlobals
+ SnelstartImport.SepaDirectCoreScheme: [sdcrRows] :: SepaDirectCoreResults -> [SepaDirectCoreScheme]
+ SnelstartImport.SepaDirectCoreScheme: data SepaDirectCoreResults
+ SnelstartImport.SepaDirectCoreScheme: data SepaGlobals
- SnelstartImport.Convert: sepaDirectCoreSchemeToING :: Text -> SepaDirectCoreScheme -> ING
+ SnelstartImport.Convert: sepaDirectCoreSchemeToING :: SepaGlobals -> SepaDirectCoreScheme -> ING
- SnelstartImport.SepaDirectCoreScheme: SepaDirectCoreScheme :: Text -> Text -> Text -> Currency -> Day -> SepaDirectCoreScheme
+ SnelstartImport.SepaDirectCoreScheme: SepaDirectCoreScheme :: Text -> Text -> Text -> Currency -> Day -> Text -> SepaDirectCoreScheme
- SnelstartImport.SepaDirectCoreScheme: readSepaDirectCoreScheme :: ByteString -> Either SepaParseErrors [SepaDirectCoreScheme]
+ SnelstartImport.SepaDirectCoreScheme: readSepaDirectCoreScheme :: ByteString -> Either SepaParseErrors SepaDirectCoreResults

Files

Changelog.md view
@@ -1,5 +1,12 @@ # Change log for template project +## Version 1.1.0++ strip whitespace from bank input++ sepa direct only should add ++ use credbttm as date++ credacct is used for bank number++ add invoice number in description (rmtinf)+ ## Version 1.0.1  + Add language files 
Readme.md view
@@ -1,7 +1,7 @@ [![https://jappieklooster.nl](https://img.shields.io/badge/blog-jappieklooster.nl-lightgrey)](https://jappieklooster.nl/tag/haskell.html)-[![Github actions build status](https://img.shields.io/github/workflow/status/jappeace/haskell-template-project/Test)](https://github.com/jappeace/haskell-template-project/actions)+[![Github actions build status](https://img.shields.io/github/workflow/status/jappeace/haskell-template-project/Test)](https://github.com/jappeace/snelstart-import/actions) [![Jappiejappie](https://img.shields.io/badge/discord-jappiejappie-black?logo=discord)](https://discord.gg/Hp4agqy)-[![Hackage version](https://img.shields.io/hackage/v/template.svg?label=Hackage)](https://hackage.haskell.org/package/template) +[![Hackage version](https://img.shields.io/hackage/v/snelstart-import.svg?label=Hackage)](https://hackage.haskell.org/package/snelstart-import)   > In lyk man is in ryk man 
snelstart-import.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack  name:           snelstart-import-version:        1.0.1+version:        1.1.0 homepage:       https://github.com/jappeace/snelstart-import#readme bug-reports:    https://github.com/jappeace/snelstart-import/issues description:    Import to snelstart from various formats such as sepa direct debit or n26 bank format. Converts the format to ING csv. Has a server for an easy UI and a cli for quick conversions.
src/SnelstartImport/Convert.hs view
@@ -11,19 +11,18 @@ import SnelstartImport.ING import SnelstartImport.N26 import Data.Text(Text)-import SnelstartImport.SepaDirectCoreScheme (SepaDirectCoreScheme(..))-import Data.Time+import SnelstartImport.SepaDirectCoreScheme -sepaDirectCoreSchemeToING :: Text -> SepaDirectCoreScheme -> ING-sepaDirectCoreSchemeToING ownAccoun SepaDirectCoreScheme{..} = ING{-  datum = UTCTime{ utctDay = dtOfSgntr, utctDayTime = 0},+sepaDirectCoreSchemeToING :: SepaGlobals -> SepaDirectCoreScheme -> ING+sepaDirectCoreSchemeToING SepaGlobals{..} SepaDirectCoreScheme{..} = ING{+  datum = creDtTm ,   naamBescrhijving = dbtr,-  rekening = ownAccoun,+  rekening = cdtrAcct,   tegenRekening  = dbtrAcct,   mutatieSoort = Overschijving, -- TODO how can we figure this out?-  bijAf = Af, -- TODO looks like it only deducts from the account, is this right?+  bijAf = Bij, -- appaerantly they ony use it for invoices so they add money   bedragEur = instdAmt ,-  mededeling = ""+  mededeling = rmtInf   }  n26ToING :: Text -> N26 -> ING
src/SnelstartImport/SepaDirectCoreScheme.hs view
@@ -1,10 +1,13 @@ {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}  -- | https://www.europeanpaymentscouncil.eu/sites/default/files/kb/file/2022-06/EPC130-08%20SDD%20Core%20C2PSP%20IG%202023%20V1.0.pdf --   this is some xml format the accountents asked support for module SnelstartImport.SepaDirectCoreScheme   ( SepaDirectCoreScheme(..)+  , SepaDirectCoreResults(..)+  , SepaGlobals(..)   , readSepaDirectCoreScheme   ) where@@ -18,7 +21,9 @@ import Data.List import Data.Bifunctor(first) import Text.Read(readMaybe)-import Data.Time(Day, parseTimeM, defaultTimeLocale)+import Data.Time(UTCTime, Day, parseTimeM, defaultTimeLocale)+import Data.Time.Format.ISO8601+import Data.Time.LocalTime(zonedTimeToUTC)  data SepaDirectCoreScheme = SepaDirectCoreScheme {   -- -- | Unambiguous identification of the account of the@@ -26,12 +31,23 @@   -- -- result of the payment transaction.   -- cdtrAcct :: Text,   endToEndId :: Text,-  dbtrAcct :: Text,-  dbtr :: Text,+  dbtrAcct :: Text, -- | bank number+  dbtr :: Text, -- | name of person sending   instdAmt :: Currency,-  dtOfSgntr :: Day+  dtOfSgntr :: Day, -- | this is not the actual transaction date+  rmtInf :: Text -- | invoice number   } deriving Show +data SepaGlobals = SepaGlobals {+  creDtTm :: UTCTime,+  cdtrAcct :: Text+  }++data SepaDirectCoreResults = SepaDirectCoreResults {+  sdcrRows :: [SepaDirectCoreScheme],+  sdcrGlob :: SepaGlobals+  }+ name_ :: Node -> Text name_ = Text.toLower . decodeUtf8 . name @@ -42,18 +58,30 @@                      | SepaParseIssues SepaIssues                      deriving Show -readSepaDirectCoreScheme :: ByteString -> Either SepaParseErrors [SepaDirectCoreScheme]+readSepaDirectCoreScheme :: ByteString -> Either SepaParseErrors SepaDirectCoreResults readSepaDirectCoreScheme contents = do   nodeRes <- first ParseXmlError $ parse contents -  traverse (first SepaParseIssues . parseSepa) (((dig "DrctDbtTxInf")) =<< ((dig "PmtInf") =<< ((dig "CstmrDrctDbtInitn") =<< dig "document" nodeRes)))+  mainNode :: Node <- first SepaParseIssues $ assertOne "CstmrDrctDbtInitn" ((dig "CstmrDrctDbtInitn") =<< dig "document" nodeRes)++  sdcrGlob <- first SepaParseIssues $ parseGlobals mainNode++  sdcrRows <- traverse (first SepaParseIssues . parseSepa) $ ((dig "DrctDbtTxInf")) =<< (dig "PmtInf" mainNode )++  pure $ SepaDirectCoreResults {..} --  data SepaIssues = ExpectedOne [Node] Text                 | ExpectedNumber Node Text                 | ExpectedDate Node Text+                | ExpectedTime Node Text                 deriving Show +parseGlobals :: Node -> Either SepaIssues SepaGlobals+parseGlobals node = do+  creDtTm <- parseTime =<< assertOne "CreDtTm" (dig "CreDtTm" =<< dig "GrpHdr" node)+  cdtrAcct <- inner_ <$>  assertOne "CdtrAcct" (dig "IBAN" =<< dig "Id" =<< dig "CdtrAcct" =<< dig "PmtInf" node)+  pure $ SepaGlobals {..}  assertOne :: Text -> [Node] -> Either SepaIssues Node assertOne label nodes =@@ -74,6 +102,11 @@   Nothing -> Left $ ExpectedDate node (inner_ node)   Just day -> Right day +parseTime :: Node -> Either SepaIssues UTCTime+parseTime node = case zonedTimeToUTC <$> iso8601ParseM (Text.unpack (inner_ node)) of+  Nothing -> Left $ ExpectedTime node (inner_ node)+  Just day -> Right day+ parseSepa :: Node -> Either SepaIssues SepaDirectCoreScheme parseSepa node = do   dbtr <- inner_ <$> assertOne "dbtr" (dig "nm" =<< dig "dbtr" node)@@ -81,6 +114,7 @@   endToEndId <- inner_ <$> assertOne "endToEndId" (dig "EndToEndId" =<< dig "PmtId" node)   instdAmt <- parseCurrency =<< assertOne "instdAmt" (dig "instdAmt" node)   dtOfSgntr <- parseDay =<< assertOne "dtOfSgntr" (dig "DtOfSgntr" =<< dig "MndtRltdInf" =<< dig "DrctDbtTx" node)+  rmtInf <- inner_ <$> assertOne "RmtInf" (dig "Ustrd" =<< dig "RmtInf" node)   Right $ SepaDirectCoreScheme {     ..     }
src/SnelstartImport/Web/Handler.hs view
@@ -24,12 +24,13 @@ import Data.ByteString.Base64 import Data.Base64.Types(extractBase64) import qualified Data.Text as Text-import SnelstartImport.SepaDirectCoreScheme(readSepaDirectCoreScheme)+import SnelstartImport.SepaDirectCoreScheme(readSepaDirectCoreScheme, sdcrRows, sdcrGlob  ) import SnelstartImport.Web.Layout(layout) import Yesod.Core(lucius) import SnelstartImport.Web.Message import Data.Time import Control.Monad.IO.Class(liftIO)+import Data.Maybe(fromMaybe)   type Form a = Html -> MForm Handler (FormResult a, Widget)@@ -42,7 +43,7 @@  inputFileForm :: Form InputFileForm inputFileForm csrf = do-  (bankRes, bankView) <- mreq textField "own bank account" Nothing+  (bankRes, bankView) <- mopt textField "own bank account" Nothing   (inputRes, inputView) <- mreq fileField "xml file" Nothing    let view = do@@ -66,7 +67,7 @@         <div>           <button type=submit >_{MsgConvert}   |]-  pure $ (InputFileForm <$> bankRes <*> inputRes, view)+  pure $ (InputFileForm <$> (Text.replace " " "" . Text.strip . fromMaybe "" <$> bankRes) <*> inputRes, view)  getRootR :: Handler Html getRootR = do@@ -104,7 +105,7 @@       if Text.isSuffixOf "xml" filename then         case readSepaDirectCoreScheme contents of           Left err -> layout $ inputForm [pack $ show err] enctype form-          Right res' -> renderDownload formRes (sepaDirectCoreSchemeToING (ifBank formRes) <$> res')+          Right res' -> renderDownload formRes (sepaDirectCoreSchemeToING (sdcrGlob res') <$> sdcrRows res')       else case readN26BS $ LBS.fromStrict contents of         Left err -> layout $ inputForm [pack err] enctype form         Right n26 -> renderDownload formRes (n26ToING (ifBank formRes) <$> toList n26)