bgmax (empty) → 0.1.0.0
raw patch · 5 files changed
+436/−0 lines, 5 filesdep +attoparsecdep +basedep +bytestringsetup-changed
Dependencies added: attoparsec, base, bytestring, time
Files
- LICENSE +30/−0
- Setup.hs +2/−0
- bgmax.cabal +34/−0
- src/Finance/BgMax/Parser.hs +185/−0
- src/Finance/BgMax/TK.hs +185/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2014, Petter Bergman++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Petter Bergman nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ bgmax.cabal view
@@ -0,0 +1,34 @@+-- Initial bgmax.cabal generated by cabal init. For further documentation,+-- see http://haskell.org/cabal/users-guide/++name: bgmax+version: 0.1.0.0+synopsis: Parse BgMax-files+homepage: http://petterbergman.se/bgmax.html+license: BSD3+license-file: LICENSE+author: Petter Bergman+maintainer: jon.petter.bergman@gmail.com+-- copyright: +category: Finance+build-type: Simple+-- extra-source-files: +cabal-version: >=1.10+description: + A low-level parser for + <http://www.bgc.se/upload/Gemensamt/Trycksaker/Manualer/BG6040.pdf BgMax>+ -files from <http://www.bgc.se Bankgirocentralen> (BGC).+ It is able to parse all of the + <http://www.bgc.se/templates/Iframe____3125.aspx example files> from BGC.++library+ exposed-modules: Finance.BgMax.TK + Finance.BgMax.Parser+ -- other-modules: + other-extensions: OverloadedStrings+ build-depends: base >=4.7 && <4.8,+ time >=1.4.2,+ bytestring >=0.10.4.0,+ attoparsec >=0.12.1.2+ hs-source-dirs: src+ default-language: Haskell98
+ src/Finance/BgMax/Parser.hs view
@@ -0,0 +1,185 @@+{-# LANGUAGE OverloadedStrings #-}+{- |+Module : $Header$+Description : Parser for the BgMax file format+Copyright : (c) Petter Bergman+License : BSD3++Maintainer : jon.petter.bergman@gmail.com+Stability : experimental+Portability : non-portable (OverloadedStrings)++This module contains a Parser for the +<http://www.bgc.se/upload/Gemensamt/Trycksaker/Manualer/BG6040.pdf BgMax>+format.++The parser tries to be forgiving with input it doesn't understand.+-}+module Finance.BgMax.Parser(parseBgMax) where+import Finance.BgMax.TK+import Data.ByteString.Char8(ByteString,dropWhile,reverse,readInt,readInteger,null,replicate)+import Data.Time(LocalTime(..),Day,TimeOfDay(..),fromGregorian)+import Data.Char(isSpace)+import Data.Attoparsec.ByteString.Char8(Parser,take,endOfLine,sepBy,string,skipWhile,notInClass)+import Prelude hiding (take,dropWhile,reverse,null,replicate)+import Control.Applicative((<|>),(*>),(<*>),(<$>),(<*))+import Data.Fixed(Pico)++class N a where+ readN :: ByteString -> Maybe (a,ByteString)++instance N Int where+ readN = readInt++instance N Integer where+ readN = readInteger+ +readFullN :: N a => ByteString -> Parser a+readFullN bs = + case readN bs of+ Nothing -> fail $ "could not read as Int: " ++ show bs+ Just (x,bs') | null bs' -> return x+ | otherwise -> fail $ "could not read all characters as Int: " ++ show bs'++n' :: N a => ByteString -> Parser a+n' = readFullN++n :: N a => Int -> Parser a+n c = take c >>= n'++nhb' :: N a => ByteString -> Parser a+nhb' = readFullN . dropWhile isSpace++nhb :: N a => Int -> Parser a+nhb c = take c >>= nhb'++orBlank :: (Int -> Parser a) -> Int -> Parser (Maybe a)+orBlank p c = (fmap Just $ p c) <|> (string (replicate c ' ') *> (return Nothing))++avb' :: ByteString -> ByteString+avb' = reverse . dropWhile isSpace . reverse++avb :: Int -> Parser ByteString+avb = fmap avb' . take++ab' :: ByteString -> ByteString+ab' = dropWhile isSpace++ab :: Int -> Parser ByteString+ab = fmap ab' . take++pOrT :: Parser TestMark+pOrT =+ "T" *> return T <|>+ "P" *> return P++parseCurrency :: Parser Currency+parseCurrency =+ "EUR" *> return EUR <|>+ "SEK" *> return SEK++makeReference :: Int -> ByteString -> Parser Reference+makeReference 0 _ = return Ref0+makeReference 1 _ = return Ref1+makeReference 2 bs = fmap Ref2 $ nhb' bs+makeReference 3 bs = return $ Ref3 $ ab' bs+makeReference 4 bs = return $ Ref4 $ avb' bs+makeReference 5 bs = return $ Ref5 $ ab' bs+makeReference i bs = return $ Ref i bs++parseDepositType :: Parser DepositType+parseDepositType = "K" *> return K <|>+ "D" *> return D <|>+ "S" *> return S++parseDay :: Parser Day+parseDay = fromGregorian <$> n 4 <*> n 2 <*> n 2++parseMicroseconds :: Parser Pico+parseMicroseconds =+ do+ second <- n 2+ microsecond <- n 6+ return $ (fromInteger second) + ((fromInteger microsecond) / 1000000)++parseTimeOfDay :: Parser TimeOfDay+parseTimeOfDay = TimeOfDay <$> n 2 <*> n 2 <*> parseMicroseconds++localTime :: Parser LocalTime+localTime = LocalTime <$> parseDay <*> parseTimeOfDay++parseTransaction :: Parser Transaction+parseTransaction = + do+ sBG <- n 10+ ref' <- take 25+ amm <- n 18+ refCode <- n 1+ chan <- n 1+ bglNo <- n 12+ imag <- n 1+ ref <- makeReference refCode ref'+ return $ Transaction sBG ref amm chan bglNo imag++parsePost :: Parser a -> Parser b -> Parser b+parsePost code make = code *> make <* skipWhile (== ' ')++tk01 :: Parser Post+tk01 = fmap TK01 $ parsePost "01" $ TK01_t <$> avb 20 <*> n 2 <*> localTime <*> pOrT++tk05 :: Parser Post+tk05 = fmap TK05 $ parsePost "05" $ TK05_t <$> n 10 <*> orBlank n 10 <*> parseCurrency++tk20 :: Parser Post+tk20 = fmap TK20 $ parsePost "20" $ TK20_t <$> parseTransaction++tk21 :: Parser Post+tk21 = fmap TK21 $ parsePost "21" $ TK21_t <$> parseTransaction <*> n 1++tk22 :: Parser Post+tk22 = fmap TK22 $ parsePost "22" $ TK22_t <$> parseTransaction++tk23 :: Parser Post+tk23 = fmap TK23 $ parsePost "23" $ TK23_t <$> parseTransaction++tk25 :: Parser Post+tk25 = fmap TK25 $ parsePost "25" $ TK25_t <$> ab 50++tk26 :: Parser Post+tk26 = fmap TK26 $ parsePost "26" $ TK26_t <$> avb 35 <*> avb 35++tk27 :: Parser Post+tk27 = fmap TK27 $ parsePost "27" $ TK27_t <$> avb 35 <*> avb 9++tk28 :: Parser Post+tk28 = fmap TK28 $ parsePost "28" $ TK28_t <$> avb 35 <*> avb 35 <*> avb 2++tk29 :: Parser Post+tk29 = fmap TK29 $ parsePost "29" $ TK29_t <$> n 12++tk15 :: Parser Post+tk15 = fmap TK15 $ parsePost "15" $ TK15_t <$> n 35 <*> parseDay <*> n 5 <*> n 18 <*> parseCurrency <*> n 8 <*> orBlank (const parseDepositType) 1++tk70 :: Parser Post+tk70 = fmap TK70 $ parsePost "70" $ TK70_t <$> n 8 <*> n 8 <*> n 8 <*> n 8++tk :: Parser Post+tk = (TK <$> n 2) <* skipWhile (notInClass "\n\r")++-- | Parser for BgMax posts.+parseBgMax :: Parser [Post]+parseBgMax = sepBy go endOfLine + where go = tk01 <|> + tk05 <|> + tk20 <|> + tk21 <|> + tk22 <|> + tk23 <|> + tk25 <|>+ tk26 <|>+ tk27 <|>+ tk28 <|>+ tk29 <|>+ tk15 <|> + tk70 <|>+ tk
+ src/Finance/BgMax/TK.hs view
@@ -0,0 +1,185 @@+{- |+Module : $Header$+Description : Datatypes for the BgMax file format+Copyright : (c) Petter Bergman+License : BSD3++Maintainer : jon.petter.bergman@gmail.com+Stability : experimental+Portability : portable++This module contain data definitions for the different types of "Posts" or +"Transaction Codes" that may occur in a BgMax file, as well as auxiliary+data types. ++The datatypes should map in a straightforward way to the +documentation from <http://www.bgc.se/upload/Gemensamt/Trycksaker/Manualer/BG6040.pdf Bankgirocentralen>.+-}+module Finance.BgMax.TK where+import Data.Time(LocalTime(..),Day)+import Data.ByteString(ByteString)++-- | Testmark+data TestMark = + T -- ^ Test file + | P -- ^ Production file + deriving Show++-- | BankGirot currently only supports EUR and SEK+data Currency = EUR | SEK deriving Show++-- | Reference.+-- The references correspond to the reference codes defined in+-- <http://www.bgc.se/upload/Gemensamt/Trycksaker/Manualer/BG6040.pdf the BgMax documentation>.+data Reference = + -- | No reference.+ Ref0+ -- | No reference. + | Ref1 + -- | Reference is correct OCR-no.+ | Ref2 Integer+ -- | One or more references.+ | Ref3 ByteString+ -- | One reference.+ | Ref4 ByteString+ -- | Incorrect reference.+ | Ref5 ByteString+ -- | Reserved for future use.+ | Ref Int ByteString + deriving Show++-- | Deposit type.+-- Deposit type according to bank agreement.+data DepositType = K | D | S deriving Show++-- | Transaction type reused in TK 20,21,22,23.+data Transaction = + Transaction {+ senderBG :: Integer,+ reference :: Reference,+ ammount :: Integer,+ -- | See the BGC documentation for the meaning of payment channel code.+ paymentChannel :: Int,+ bcgNo :: Integer,+ -- | See the BGC documentation for the meaning of the image mark.+ imageMark :: Int } + deriving Show++-- | Start post.+data TK01_t =+ TK01_t {+ layoutName :: ByteString,+ layoutVersion :: Int,+ timeStamp :: LocalTime,+ testMark :: TestMark } + deriving Show++-- | Opening post.+data TK05_t =+ TK05_t {+ recipientBG :: Integer,+ recipientPG :: Maybe Integer,+ currency :: Currency } + deriving Show++-- | Payment post.+data TK20_t =+ TK20_t {+ payment :: Transaction } + deriving Show++-- | Deduction post.+data TK21_t =+ TK21_t {+ deduction :: Transaction,+ -- | See the BGC documentation for the meaning of the deduction code.+ deductionCode :: Int } + deriving Show++-- | Extra reference post.+data TK22_t =+ TK22_t {+ positiveReference :: Transaction } + deriving Show++-- | Extra reference post (negative ammount).+data TK23_t =+ TK23_t {+ negativeReference :: Transaction } + deriving Show++-- | Information post.+data TK25_t =+ TK25_t {+ information :: ByteString } + deriving Show++-- | Name post.+data TK26_t =+ TK26_t {+ name :: ByteString,+ extraName :: ByteString } + deriving Show++-- | Address 1 post.+data TK27_t =+ TK27_t {+ address :: ByteString,+ zipCode :: ByteString } + deriving Show++-- | Address 2 post.+data TK28_t =+ TK28_t {+ city :: ByteString,+ country :: ByteString,+ countryCode :: ByteString } + deriving Show++-- | Organisation-number post.+data TK29_t = + TK29_t {+ orgNo :: Integer } + deriving Show++-- | Deposit post.+data TK15_t =+ TK15_t {+ recipientAccount :: Integer,+ paymentDate :: Day,+ depositNo :: Int,+ depositAmmount :: Integer,+ depositCurrency :: Currency,+ paymentDeductionCount :: Int,+ depositType :: Maybe DepositType } + deriving Show++-- | End post+data TK70_t =+ TK70_t {+ paymentsCount :: Int,+ deductionsCount :: Int,+ extraReferenceCount :: Int,+ depositCount :: Int } + deriving Show++-- | Sum type for posts.+data Post =+ TK01 TK01_t+ | TK05 TK05_t+ | TK20 TK20_t+ | TK21 TK21_t+ | TK22 TK22_t+ | TK23 TK23_t+ | TK25 TK25_t+ | TK26 TK26_t+ | TK27 TK27_t+ | TK28 TK28_t+ | TK29 TK29_t+ | TK15 TK15_t+ | TK70 TK70_t+ -- | The BGC documentation states that implementations should ignore posts + -- they don't understand, they will be instead be recorded using this + -- constructor.+ | TK Int + deriving Show