diff --git a/src/Data/TITO/Parser.hs b/src/Data/TITO/Parser.hs index 6dbb909..56cc274 100644 --- a/src/Data/TITO/Parser.hs +++ b/src/Data/TITO/Parser.hs @@ -1,5 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE NamedFieldPuns #-} module Data.TITO.Parser where @@ -143,84 +144,89 @@ titoRoot = record "00" $ do return TITORoot {..} titoRecord :: Parser TITORecord -titoRecord = P.try (transaction' False) - <|> P.try (transactionDetail' False) +titoRecord = P.try (transaction False) <|> P.try balance <|> P.try summary <|> P.try fixSummary <|> P.try special <|> P.try info - <|> P.try (transaction' True) - <|> P.try (transactionDetail' True) + <|> P.try (transaction True) -transaction' :: Bool -> Parser TITORecord -transaction' isInformational = record (if isInformational then "80" else "10") $ do - transactionNumber <- numeric 6 - archiveId <- optional' alphaNumeric 18 - parsedDate <- date - valueDate <- optional 6 date - paymentDate <- optional 6 date - transactionType <- transactionType - descriptionCode <- alphaNumeric 3 - descriptionText <- alphaNumeric 35 - amount <- money - receiptCode <- alphaNumeric 1 - transferMethod <- alphaNumeric 1 - payeeName <- optional' alphaNumeric 35 - nameSource <- optional 1 nameSource - recipientAccount <- optional' alphaNumeric 14 - accountChanged <- optional' alphaNumeric 1 - reference <- optional' numeric 20 - formNumber <- optional' alphaNumeric 8 - depth <- alphaNumeric 1 - return Transaction {date = parsedDate, ..} +transaction :: Bool -> Parser TITORecord +transaction = fmap . TransactionRecord <*> transaction' 0 -transactionDetail' :: Bool -> Parser TITORecord +transaction' :: Int -> Bool -> Parser Transaction +transaction' minimumDepth isInformational = do + (depth, transaction) <- record (if isInformational then "80" else "10") $ do + transactionNumber <- numeric 6 + archiveId <- optional' alphaNumeric 18 + parsedDate <- date + valueDate <- optional 6 date + paymentDate <- optional 6 date + transactionType <- transactionType + descriptionCode <- alphaNumeric 3 + descriptionText <- alphaNumeric 35 + amount <- money + receiptCode <- alphaNumeric 1 + transferMethod <- alphaNumeric 1 + payeeName <- optional' alphaNumeric 35 + nameSource <- optional 1 nameSource + recipientAccount <- optional' alphaNumeric 14 + accountChanged <- optional' alphaNumeric 1 + reference <- optional' numeric 20 + formNumber <- optional' alphaNumeric 8 + depth <- fromIntegral <$> numeric 1 <|> const 0 <$> P.char 32 -- space + guard $ depth >= minimumDepth + return (depth, Transaction {details = [], itemisation = [], date = parsedDate, ..}) + details <- P.many $ P.try $ transactionDetail' isInformational + itemisation <- P.many $ P.try $ transaction' (depth + 1) isInformational + pure $ transaction {details, itemisation} + +transactionDetail' :: Bool -> Parser TransactionDetail transactionDetail' isInformational = record' (if isInformational then "81" else "11") $ \recordLength -> do let detailLength = recordLength - 8 detailType <- optional' alphaNumeric 2 - detail <- case detailType of - Just "00" -> do - first <- alphaNumeric 35 - rest <- P.count' 0 11 (optional' alphaNumeric 35) - pure $ Freeform $ first :| catMaybes rest - Just "01" -> Number <$> numeric 8 - Just "02" -> do - customer <- alphaNumeric 10 - alphaNumeric 1 - invoice <- alphaNumeric 15 - alphaNumeric 1 - date <- date - pure Invoice {..} - Just "03" -> Card <$> alphaNumeric 19 <* alphaNumeric 1 <*> optional' alphaNumeric 14 - Just "04" -> Fix <$> alphaNumeric 18 - Just "05" -> do - foreignAmount <- money - alphaNumeric 1 - currency <- alphaNumeric 3 - alphaNumeric 1 - exchangeRate <- numeric 11 - exchangeReference <- optional' alphaNumeric 6 - pure ForeignCurrency {..} - Just "06" -> fmap Notes $ (:|) <$> alphaNumeric 35 <*> (maybeToList <$> optional' alphaNumeric 35) - Just "07" -> do - first <- alphaNumeric 35 - rest <- P.count' 0 11 (optional' alphaNumeric 35) - pure $ BankFreeform $ first :| catMaybes rest - Just "08" -> PaymentSubject <$> numeric 3 <* alphaNumeric 1 <*> alphaNumeric 31 - Just "09" -> Name <$> alphaNumeric 35 - Just "11" -> do - payersReference <- optional' alphaNumeric 35 - payeeIBAN <- optional' alphaNumeric 35 - payeeBIC <- optional' alphaNumeric 35 - payeeName <- optional' alphaNumeric 70 - payerName <- optional' alphaNumeric 70 - payerId <- optional' alphaNumeric 35 - archiveId <- optional' alphaNumeric 35 - pure SEPA {..} - _ -> Unknown <$> alphaNumeric detailLength - pure TransactionDetail {..} + case detailType of + Just "00" -> do + first <- alphaNumeric 35 + rest <- P.count' 0 11 (optional' alphaNumeric 35) + pure $ Freeform $ first :| catMaybes rest + Just "01" -> Number <$> numeric 8 + Just "02" -> do + customer <- alphaNumeric 10 + alphaNumeric 1 + invoice <- alphaNumeric 15 + alphaNumeric 1 + date <- date + pure Invoice {..} + Just "03" -> Card <$> alphaNumeric 19 <* alphaNumeric 1 <*> optional' alphaNumeric 14 + Just "04" -> Fix <$> alphaNumeric 18 + Just "05" -> do + foreignAmount <- money + alphaNumeric 1 + currency <- alphaNumeric 3 + alphaNumeric 1 + exchangeRate <- numeric 11 + exchangeReference <- optional' alphaNumeric 6 + pure ForeignCurrency {..} + Just "06" -> fmap Notes $ (:|) <$> alphaNumeric 35 <*> (maybeToList <$> optional' alphaNumeric 35) + Just "07" -> do + first <- alphaNumeric 35 + rest <- P.count' 0 11 (optional' alphaNumeric 35) + pure $ BankFreeform $ first :| catMaybes rest + Just "08" -> PaymentSubject <$> numeric 3 <* alphaNumeric 1 <*> alphaNumeric 31 + Just "09" -> Name <$> alphaNumeric 35 + Just "11" -> do + payersReference <- optional' alphaNumeric 35 + payeeIBAN <- optional' alphaNumeric 35 + payeeBIC <- optional' alphaNumeric 35 + payeeName <- optional' alphaNumeric 70 + payerName <- optional' alphaNumeric 70 + payerId <- optional' alphaNumeric 35 + archiveId <- optional' alphaNumeric 35 + pure SEPA {..} + _ -> Unknown <$> alphaNumeric detailLength balance :: Parser TITORecord balance = record "40" $ do diff --git a/src/Data/TITO/Types.hs b/src/Data/TITO/Types.hs index 49d60fb..ffa9956 100644 --- a/src/Data/TITO/Types.hs +++ b/src/Data/TITO/Types.hs @@ -34,31 +34,9 @@ data TITORoot = TITORoot -- T00 , ibanAndBic :: Maybe (Text, Text) } deriving (Show, Eq) -data TITORecord = Transaction -- T80 if isInformational, else T10 +data TITORecord = TransactionRecord -- T80 if isInformational, else T10 { isInformational :: Bool - , transactionNumber :: Integer - , archiveId :: Maybe Text - , date :: Day - , valueDate :: Maybe Day - , paymentDate :: Maybe Day - , transactionType :: TransactionType - , descriptionCode :: Text - , descriptionText :: Text - , amount :: Money - , receiptCode :: Text - , transferMethod :: Text - , payeeName :: Maybe Text - , nameSource :: Maybe NameSource - , recipientAccount :: Maybe Text - , accountChanged :: Maybe Text - , reference :: Maybe Integer - , formNumber :: Maybe Text - , depth :: Text - } - | TransactionDetail -- T81 if isInformational, else T11 - { isInformational :: Bool - , detailType :: Maybe Text - , detail :: TransactionDetail + , transaction :: Transaction } | Balance -- T40 { date :: Day @@ -91,6 +69,28 @@ data TITORecord = Transaction -- T80 if isInformational, else T10 } deriving (Show, Eq) +data Transaction = Transaction + { transactionNumber :: Integer + , archiveId :: Maybe Text + , date :: Day + , valueDate :: Maybe Day + , paymentDate :: Maybe Day + , transactionType :: TransactionType + , descriptionCode :: Text + , descriptionText :: Text + , amount :: Money + , receiptCode :: Text + , transferMethod :: Text + , payeeName :: Maybe Text + , nameSource :: Maybe NameSource + , recipientAccount :: Maybe Text + , accountChanged :: Maybe Text + , reference :: Maybe Integer + , formNumber :: Maybe Text + , details :: [TransactionDetail] + , itemisation :: [Transaction] + } deriving (Show, Eq) + data TransactionDetail = Freeform (NonEmpty Text) -- 00 | Number Integer -- 01 | Invoice -- 02