summaryrefslogtreecommitdiffstats
path: root/app/Import/Ing/CurrentAccountCsv.hs
diff options
context:
space:
mode:
authorRutger Broekhoff2025-03-22 14:52:35 +0100
committerRutger Broekhoff2025-03-22 14:52:35 +0100
commit5493329b2eed7e151f4a323c108caad2253b08bb (patch)
treea8fd1a58e0ba77d06e75222034def5eb49043bb6 /app/Import/Ing/CurrentAccountCsv.hs
parente40e290ef216656d304f4f3095dbef223e94191d (diff)
downloadrdcapsis-5493329b2eed7e151f4a323c108caad2253b08bb.tar.gz
rdcapsis-5493329b2eed7e151f4a323c108caad2253b08bb.zip
Refactor parser for current account statement
Diffstat (limited to 'app/Import/Ing/CurrentAccountCsv.hs')
-rw-r--r--app/Import/Ing/CurrentAccountCsv.hs44
1 files changed, 22 insertions, 22 deletions
diff --git a/app/Import/Ing/CurrentAccountCsv.hs b/app/Import/Ing/CurrentAccountCsv.hs
index bf28730..1456be1 100644
--- a/app/Import/Ing/CurrentAccountCsv.hs
+++ b/app/Import/Ing/CurrentAccountCsv.hs
@@ -17,12 +17,12 @@ import Data.Time.Zones (TZ, loadTZFromDB)
17import Data.Vector qualified as V 17import Data.Vector qualified as V
18import Import.Ing.Shared 18import Import.Ing.Shared
19 ( DebitCredit (Credit, Debit), 19 ( DebitCredit (Credit, Debit),
20 dateCP,
21 decimalCP,
22 ibanCP,
23 maybeCP, 20 maybeCP,
21 parseDateM,
22 parseDecimalM,
23 parseIbanM,
24 parseTimestampM,
24 scsvOptions, 25 scsvOptions,
25 timestampCP,
26 ) 26 )
27import System.IO (Handle) 27import System.IO (Handle)
28import Text.Regex.TDFA ((=~~)) 28import Text.Regex.TDFA ((=~~))
@@ -155,7 +155,7 @@ maybeNotProvided :: T.Text -> Maybe T.Text
155maybeNotProvided t = if t == "NOTPROVIDED" then Nothing else Just t 155maybeNotProvided t = if t == "NOTPROVIDED" then Nothing else Just t
156 156
157valueDateCP :: T.Text -> C.Parser Day 157valueDateCP :: T.Text -> C.Parser Day
158valueDateCP = dateCP "%d/%m/%Y" 158valueDateCP = parseDateM "%d/%m/%Y"
159 159
160data PartTx = PartTx !Day !TransactionType !DebitCredit 160data PartTx = PartTx !Day !TransactionType !DebitCredit
161 161
@@ -163,7 +163,7 @@ notificationsCP :: TZ -> PartTx -> T.Text -> C.Parser MoreData
163notificationsCP _ (PartTx _ Transfer Credit) t = do 163notificationsCP _ (PartTx _ Transfer Credit) t = do
164 let regex = "^Name: (.*) Description: (.*) IBAN: ([A-Z0-9]+) Reference: (.*) Value date: ([0-9]{2}/[0-9]{2}/[0-9]{4})$" :: String 164 let regex = "^Name: (.*) Description: (.*) IBAN: ([A-Z0-9]+) Reference: (.*) Value date: ([0-9]{2}/[0-9]{2}/[0-9]{4})$" :: String
165 (_, _, _, [name, desc, ibanTxt, ref, valDateTxt]) <- t =~~ regex :: C.Parser (T.Text, T.Text, T.Text, [T.Text]) 165 (_, _, _, [name, desc, ibanTxt, ref, valDateTxt]) <- t =~~ regex :: C.Parser (T.Text, T.Text, T.Text, [T.Text])
166 iban <- ibanCP ibanTxt 166 iban <- parseIbanM ibanTxt
167 valDate <- valueDateCP valDateTxt 167 valDate <- valueDateCP valDateTxt
168 return $ 168 return $
169 DepositTransferData 169 DepositTransferData
@@ -185,7 +185,7 @@ notificationsCP _ (PartTx _ Transfer Debit) t = do
185notificationsCP amsTz (PartTx _ PaymentTerminal Debit) t = do 185notificationsCP amsTz (PartTx _ PaymentTerminal Debit) t = do
186 let regex = "^Card sequence no.: ([0-9]+) ? ([0-9]{2}/[0-9]{2}/[0-9]{4} [0-9]{2}:[0-9]{2}) Transaction: (.*) Term: ((.+) Google Pay|(.+)) Value date: ([0-9]{2}/[0-9]{2}/[0-9]{4})$" :: String 186 let regex = "^Card sequence no.: ([0-9]+) ? ([0-9]{2}/[0-9]{2}/[0-9]{4} [0-9]{2}:[0-9]{2}) Transaction: (.*) Term: ((.+) Google Pay|(.+)) Value date: ([0-9]{2}/[0-9]{2}/[0-9]{4})$" :: String
187 (_, _, _, [cardSeqNo, timestampTxt, transaction, _, gpayTerm, noGpayTerm, valDateTxt]) <- t =~~ regex :: C.Parser (T.Text, T.Text, T.Text, [T.Text]) 187 (_, _, _, [cardSeqNo, timestampTxt, transaction, _, gpayTerm, noGpayTerm, valDateTxt]) <- t =~~ regex :: C.Parser (T.Text, T.Text, T.Text, [T.Text])
188 timestamp <- timestampCP "%d/%m/%Y %H:%M" amsTz timestampTxt 188 timestamp <- parseTimestampM "%d/%m/%Y %H:%M" amsTz timestampTxt
189 valDate <- valueDateCP valDateTxt 189 valDate <- valueDateCP valDateTxt
190 return $ 190 return $
191 PaymentTerminalData 191 PaymentTerminalData
@@ -199,7 +199,7 @@ notificationsCP amsTz (PartTx _ PaymentTerminal Debit) t = do
199notificationsCP amsTz (PartTx _ PaymentTerminal Credit) t = do 199notificationsCP amsTz (PartTx _ PaymentTerminal Credit) t = do
200 let regex = "^Card sequence no.: ([0-9]+) ? ([0-9]{2}/[0-9]{2}/[0-9]{4} [0-9]{2}:[0-9]{2}) Transaction: (.*) Term: (.*) Cashback transaction Value date: ([0-9]{2}/[0-9]{2}/[0-9]{4})$" :: String 200 let regex = "^Card sequence no.: ([0-9]+) ? ([0-9]{2}/[0-9]{2}/[0-9]{4} [0-9]{2}:[0-9]{2}) Transaction: (.*) Term: (.*) Cashback transaction Value date: ([0-9]{2}/[0-9]{2}/[0-9]{4})$" :: String
201 (_, _, _, [cardSeqNo, timestampTxt, transaction, term, valDateTxt]) <- t =~~ regex :: C.Parser (T.Text, T.Text, T.Text, [T.Text]) 201 (_, _, _, [cardSeqNo, timestampTxt, transaction, term, valDateTxt]) <- t =~~ regex :: C.Parser (T.Text, T.Text, T.Text, [T.Text])
202 timestamp <- timestampCP "%d/%m/%Y %H:%M" amsTz timestampTxt 202 timestamp <- parseTimestampM "%d/%m/%Y %H:%M" amsTz timestampTxt
203 valDate <- valueDateCP valDateTxt 203 valDate <- valueDateCP valDateTxt
204 return $ 204 return $
205 PaymentTerminalCashbackData 205 PaymentTerminalCashbackData
@@ -212,8 +212,8 @@ notificationsCP amsTz (PartTx _ PaymentTerminal Credit) t = do
212notificationsCP amsTz (PartTx _ OnlineBanking Credit) t = do 212notificationsCP amsTz (PartTx _ OnlineBanking Credit) t = do
213 let regex = "^Name: (.*) Description: (.*) IBAN: ([A-Z0-9]+) Date/time: ([0-9]{2}-[0-9]{2}-[0-9]{4} [0-9]{2}:[0-9]{2}:[0-9]{2}) Value date: ([0-9]{2}/[0-9]{2}/[0-9]{4})$" :: String 213 let regex = "^Name: (.*) Description: (.*) IBAN: ([A-Z0-9]+) Date/time: ([0-9]{2}-[0-9]{2}-[0-9]{4} [0-9]{2}:[0-9]{2}:[0-9]{2}) Value date: ([0-9]{2}/[0-9]{2}/[0-9]{4})$" :: String
214 (_, _, _, [name, desc, ibanTxt, timestampTxt, valDateTxt]) <- t =~~ regex :: C.Parser (T.Text, T.Text, T.Text, [T.Text]) 214 (_, _, _, [name, desc, ibanTxt, timestampTxt, valDateTxt]) <- t =~~ regex :: C.Parser (T.Text, T.Text, T.Text, [T.Text])
215 iban <- ibanCP ibanTxt 215 iban <- parseIbanM ibanTxt
216 timestamp <- timestampCP "%d-%m-%Y %H:%M:%S" amsTz timestampTxt 216 timestamp <- parseTimestampM "%d-%m-%Y %H:%M:%S" amsTz timestampTxt
217 valDate <- valueDateCP valDateTxt 217 valDate <- valueDateCP valDateTxt
218 return $ 218 return $
219 OnlineBankingCredit 219 OnlineBankingCredit
@@ -226,11 +226,11 @@ notificationsCP amsTz (PartTx _ OnlineBanking Credit) t = do
226notificationsCP amsTz (PartTx _ OnlineBanking Debit) t = do 226notificationsCP amsTz (PartTx _ OnlineBanking Debit) t = do
227 let regex = "^Name: (.*) Description: (.*) IBAN: ([A-Z0-9]+) (Date/time: ([0-9]{2}-[0-9]{2}-[0-9]{4} [0-9]{2}:[0-9]{2}:[0-9]{2}) )?Value date: ([0-9]{2}/[0-9]{2}/[0-9]{4})$" :: String 227 let regex = "^Name: (.*) Description: (.*) IBAN: ([A-Z0-9]+) (Date/time: ([0-9]{2}-[0-9]{2}-[0-9]{4} [0-9]{2}:[0-9]{2}:[0-9]{2}) )?Value date: ([0-9]{2}/[0-9]{2}/[0-9]{4})$" :: String
228 (_, _, _, [name, desc, ibanTxt, _, timestampTxt, valDateTxt]) <- t =~~ regex :: C.Parser (T.Text, T.Text, T.Text, [T.Text]) 228 (_, _, _, [name, desc, ibanTxt, _, timestampTxt, valDateTxt]) <- t =~~ regex :: C.Parser (T.Text, T.Text, T.Text, [T.Text])
229 iban <- ibanCP ibanTxt 229 iban <- parseIbanM ibanTxt
230 timestamp <- 230 timestamp <-
231 if T.null timestampTxt 231 if T.null timestampTxt
232 then pure Nothing 232 then pure Nothing
233 else Just <$> timestampCP "%d-%m-%Y %H:%M:%S" amsTz timestampTxt 233 else Just <$> parseTimestampM "%d-%m-%Y %H:%M:%S" amsTz timestampTxt
234 valDate <- valueDateCP valDateTxt 234 valDate <- valueDateCP valDateTxt
235 return $ 235 return $
236 OnlineBankingDebit 236 OnlineBankingDebit
@@ -245,7 +245,7 @@ notificationsCP _ (PartTx date DirectDebit Debit) t = normalRecurrentDirectDebit
245 normalRecurrentDirectDebit = do 245 normalRecurrentDirectDebit = do
246 let regex = "^Name: (.*) Description: (.*) IBAN: ([A-Z0-9]+) Reference: (.*) Mandate ID: (.*) Creditor ID: (.*) Recurrent SEPA direct debit (Other party: (.*) )?Value date: ([0-9]{2}/[0-9]{2}/[0-9]{4})$" :: String 246 let regex = "^Name: (.*) Description: (.*) IBAN: ([A-Z0-9]+) Reference: (.*) Mandate ID: (.*) Creditor ID: (.*) Recurrent SEPA direct debit (Other party: (.*) )?Value date: ([0-9]{2}/[0-9]{2}/[0-9]{4})$" :: String
247 (_, _, _, [name, desc, ibanTxt, ref, mandateId, creditorId, _, otherParty, valDateTxt]) <- t =~~ regex :: C.Parser (T.Text, T.Text, T.Text, [T.Text]) 247 (_, _, _, [name, desc, ibanTxt, ref, mandateId, creditorId, _, otherParty, valDateTxt]) <- t =~~ regex :: C.Parser (T.Text, T.Text, T.Text, [T.Text])
248 iban <- ibanCP ibanTxt 248 iban <- parseIbanM ibanTxt
249 valDate <- valueDateCP valDateTxt 249 valDate <- valueDateCP valDateTxt
250 return $ 250 return $
251 RecurrentDirectDebitData 251 RecurrentDirectDebitData
@@ -261,7 +261,7 @@ notificationsCP _ (PartTx date DirectDebit Debit) t = normalRecurrentDirectDebit
261 ingInsurancePayment = do 261 ingInsurancePayment = do
262 let regex = "^Name: (.* ING Verzekeren) Description: (.*) IBAN: ([A-Z0-9]+) Reference: (.*) Mandate ID: (.*) Creditor ID: (.*) Recurrent SEPA direct debit$" :: String 262 let regex = "^Name: (.* ING Verzekeren) Description: (.*) IBAN: ([A-Z0-9]+) Reference: (.*) Mandate ID: (.*) Creditor ID: (.*) Recurrent SEPA direct debit$" :: String
263 (_, _, _, [name, desc, ibanTxt, ref, mandateId, creditorId]) <- t =~~ regex :: C.Parser (T.Text, T.Text, T.Text, [T.Text]) 263 (_, _, _, [name, desc, ibanTxt, ref, mandateId, creditorId]) <- t =~~ regex :: C.Parser (T.Text, T.Text, T.Text, [T.Text])
264 iban <- ibanCP ibanTxt 264 iban <- parseIbanM ibanTxt
265 return $ 265 return $
266 RecurrentDirectDebitData 266 RecurrentDirectDebitData
267 { rddName = name, 267 { rddName = name,
@@ -276,8 +276,8 @@ notificationsCP _ (PartTx date DirectDebit Debit) t = normalRecurrentDirectDebit
276notificationsCP amsTz (PartTx _ Ideal Debit) t = do 276notificationsCP amsTz (PartTx _ Ideal Debit) t = do
277 let regex = "^Name: (.*) Description: (.*) IBAN: ([A-Z0-9]+) Reference: ([0-9]{2}-[0-9]{2}-[0-9]{4} [0-9]{2}:[0-9]{2}) ([0-9]+) Value date: ([0-9]{2}/[0-9]{2}/[0-9]{4})$" :: String 277 let regex = "^Name: (.*) Description: (.*) IBAN: ([A-Z0-9]+) Reference: ([0-9]{2}-[0-9]{2}-[0-9]{4} [0-9]{2}:[0-9]{2}) ([0-9]+) Value date: ([0-9]{2}/[0-9]{2}/[0-9]{4})$" :: String
278 (_, _, _, [name, desc, ibanTxt, timestampTxt, ref, valDateTxt]) <- t =~~ regex :: C.Parser (T.Text, T.Text, T.Text, [T.Text]) 278 (_, _, _, [name, desc, ibanTxt, timestampTxt, ref, valDateTxt]) <- t =~~ regex :: C.Parser (T.Text, T.Text, T.Text, [T.Text])
279 iban <- ibanCP ibanTxt 279 iban <- parseIbanM ibanTxt
280 timestamp <- timestampCP "%d-%m-%Y %H:%M" amsTz timestampTxt 280 timestamp <- parseTimestampM "%d-%m-%Y %H:%M" amsTz timestampTxt
281 valDate <- valueDateCP valDateTxt 281 valDate <- valueDateCP valDateTxt
282 return $ 282 return $
283 IdealDebitData 283 IdealDebitData
@@ -291,7 +291,7 @@ notificationsCP amsTz (PartTx _ Ideal Debit) t = do
291notificationsCP _ (PartTx _ BatchPayment Credit) t = do 291notificationsCP _ (PartTx _ BatchPayment Credit) t = do
292 let regex = "^Name: (.*) Description: (.*) IBAN: ([A-Z0-9]+) Reference: (.*) Value date: ([0-9]{2}/[0-9]{2}/[0-9]{4})$" :: String 292 let regex = "^Name: (.*) Description: (.*) IBAN: ([A-Z0-9]+) Reference: (.*) Value date: ([0-9]{2}/[0-9]{2}/[0-9]{4})$" :: String
293 (_, _, _, [name, desc, ibanTxt, ref, valDateTxt]) <- t =~~ regex :: C.Parser (T.Text, T.Text, T.Text, [T.Text]) 293 (_, _, _, [name, desc, ibanTxt, ref, valDateTxt]) <- t =~~ regex :: C.Parser (T.Text, T.Text, T.Text, [T.Text])
294 iban <- ibanCP ibanTxt 294 iban <- parseIbanM ibanTxt
295 valDate <- valueDateCP valDateTxt 295 valDate <- valueDateCP valDateTxt
296 return $ 296 return $
297 BatchPaymentData 297 BatchPaymentData
@@ -310,7 +310,7 @@ debitCreditCP t = fail ("Unknown debit/credit value '" ++ T.unpack t ++ "'")
310 310
311parseNamedRecord :: TZ -> C.NamedRecord -> C.Parser PrimTx 311parseNamedRecord :: TZ -> C.NamedRecord -> C.Parser PrimTx
312parseNamedRecord amsTz m = do 312parseNamedRecord amsTz m = do
313 date <- m .: "Date" >>= dateCP "%0Y%m%d" 313 date <- m .: "Date" >>= parseDateM "%0Y%m%d"
314 debitCredit <- m .: "Debit/credit" >>= debitCreditCP 314 debitCredit <- m .: "Debit/credit" >>= debitCreditCP
315 codeText <- m .: "Code" 315 codeText <- m .: "Code"
316 tyText <- m .: "Transaction type" 316 tyText <- m .: "Transaction type"
@@ -322,11 +322,11 @@ parseNamedRecord amsTz m = do
322 else 322 else
323 PrimTx date 323 PrimTx date
324 <$> (m .: "Name / Description" <&> maybeNotProvided) 324 <$> (m .: "Name / Description" <&> maybeNotProvided)
325 <*> (m .: "Account" >>= ibanCP) 325 <*> (m .: "Account" >>= parseIbanM)
326 <*> (m .: "Counterparty" >>= maybeCP ibanCP) 326 <*> (m .: "Counterparty" >>= maybeCP parseIbanM)
327 <*> pure debitCredit 327 <*> pure debitCredit
328 <*> (m .: "Amount (EUR)" >>= decimalCP) 328 <*> (m .: "Amount (EUR)" >>= parseDecimalM)
329 <*> (m .: "Resulting balance" >>= decimalCP) 329 <*> (m .: "Resulting balance" >>= parseDecimalM)
330 <*> m .: "Tag" 330 <*> m .: "Tag"
331 <*> (m .: "Notifications" >>= notificationsCP amsTz (PartTx date ty debitCredit)) 331 <*> (m .: "Notifications" >>= notificationsCP amsTz (PartTx date ty debitCredit))
332 332