Skip to content

Commit a9edfe2

Browse files
committed
refactor: use sepa_file_parser instead of camt_parser
we used a forked `camt_parser` version which was outdated. To use the `sepa_file_parser` gem we extended the mapping factory in order to keep the code impact as small as possible. NOTE: thanks to the mapping the transaction_id is now better found which caused a few spec to break due to invalid expectations. Those were removed.
1 parent d516cff commit a9edfe2

24 files changed

+898
-182
lines changed

Gemfile

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ ruby "3.3.2"
66

77
gem "activesupport"
88
gem "camt_parser", git: "https://github.com/railslove/camt_parser.git"
9+
gem "sepa_file_parser"
910
gem "cmxl", git: "https://github.com/railslove/cmxl"
1011
gem "epics"
1112
gem "faraday"

Gemfile.lock

+8
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ GEM
5555
database_cleaner-sequel (2.0.2)
5656
database_cleaner-core (~> 2.0.0)
5757
sequel
58+
date (3.4.1)
5859
diff-lcs (1.5.1)
5960
domain_name (0.5.20190701)
6061
unf (>= 0.0.5, < 1.0.0)
@@ -210,6 +211,10 @@ GEM
210211
sentry-sidekiq (5.22.1)
211212
sentry-ruby (~> 5.22.1)
212213
sidekiq (>= 3.0)
214+
sepa_file_parser (0.4.0)
215+
bigdecimal
216+
nokogiri
217+
time
213218
sepa_king (0.12.0)
214219
activemodel (>= 3.1)
215220
iban-tools
@@ -239,6 +244,8 @@ GEM
239244
rubocop-performance (~> 1.21.0)
240245
statsd-ruby (1.5.0)
241246
tilt (2.4.0)
247+
time (0.4.1)
248+
date
242249
timecop (0.9.10)
243250
tzinfo (2.0.6)
244251
concurrent-ruby (~> 1.0)
@@ -287,6 +294,7 @@ DEPENDENCIES
287294
rubocop
288295
sentry-ruby
289296
sentry-sidekiq
297+
sepa_file_parser
290298
sepa_king
291299
sequel
292300
sidekiq

Rakefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ namespace :migration_tasks do
8686

8787
bank_statements = Box::BankStatement.where(account_id: account_id).all
8888
bank_statements.each do |bank_statement|
89-
parser = bank_statement.content.starts_with?(":") ? Cmxl : CamtParser::Format053::Statement
89+
parser = bank_statement.content.starts_with?(":") ? Cmxl : SepaFileParser::Camt053::Statement
9090
begin
9191
result = parser.parse(bank_statement.content)
9292
transactions = result.is_a?(Array) ? result.first.transactions : result.transactions

box/business_processes/import_bank_statement.rb

+2-2
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def self.find_or_create_bank_statement(raw_bank_statement, account)
5757
bs.account_id = account.id
5858
bs.sequence = raw_bank_statement.sequence
5959
bs.year = extract_year_from_bank_statement(raw_bank_statement)
60-
bs.remote_account = raw_bank_statement.account_identification.source
60+
bs.remote_account = raw_bank_statement.account_identification.iban
6161
bs.opening_balance = as_big_decimal(raw_bank_statement.opening_or_intermediary_balance) # this will be final or intermediate
6262
bs.closing_balance = as_big_decimal(raw_bank_statement.closing_or_intermediary_balance) # this will be final or intermediate
6363
bs.transaction_count = raw_bank_statement.transactions.count
@@ -85,7 +85,7 @@ def self.as_big_decimal(input)
8585

8686
def self.extract_year_from_bank_statement(raw_bank_statement)
8787
first_transaction = raw_bank_statement.transactions.first
88-
first_transaction&.date&.year
88+
first_transaction.date&.year
8989
end
9090

9191
def self.checksum(raw_bank_statement, account)

box/business_processes/import_statements.rb

+25-26
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
# frozen_string_literal: true
22

3-
require "cmxl"
4-
require "camt_parser"
3+
require "sepa_file_parser"
4+
55

66
require_relative "../models/account"
77
require_relative "../models/bank_statement"
88
require_relative "../models/event"
99
require_relative "../../lib/checksum_generator"
10+
require_relative "../../lib/data_mapping/statement_factory"
1011

1112
module Box
1213
module BusinessProcesses
1314
class ImportStatements
14-
PARSERS = {"mt940" => Cmxl, "camt53" => CamtParser::Format053::Statement}.freeze
15+
PARSERS = {"mt940" => Cmxl, "camt53" => SepaFileParser::Camt053::Statement}.freeze
1516

1617
def self.from_bank_statement(bank_statement, upcoming = false)
1718
bank_transactions = parse_bank_statement(bank_statement)
@@ -28,7 +29,9 @@ def self.from_bank_statement(bank_statement, upcoming = false)
2829
def self.parse_bank_statement(bank_statement)
2930
parser = PARSERS.fetch(bank_statement.account.statements_format, Cmxl)
3031
result = parser.parse(bank_statement.content)
31-
result.is_a?(Array) ? result.first.transactions : result.transactions
32+
statement_data = result.is_a?(Array) ? result.first : result
33+
statement = DataMapping::StatementFactory.new(statement_data, bank_statement.account).call
34+
statement.transactions
3235
end
3336

3437
def self.create_statement(bank_statement, bank_transaction, upcoming = false)
@@ -71,51 +74,47 @@ def self.checksum(transaction, bank_statement)
7174
end
7275

7376
def self.checksum_attributes(transaction, remote_account)
74-
return [remote_account, transaction.transaction_id] if transaction.try(:transaction_id).present?
77+
return [remote_account, transaction.transaction_id] if transaction.transaction_id.present?
7578

7679
payload_from_transaction_attributes(transaction, remote_account)
7780
end
7881

7982
def self.payload_from_transaction_attributes(transaction, remote_account)
80-
eref = transaction.respond_to?(:eref) ? transaction.eref : transaction.sepa["EREF"]
81-
mref = transaction.respond_to?(:mref) ? transaction.mref : transaction.sepa["MREF"]
82-
svwz = transaction.respond_to?(:svwz) ? transaction.svwz : transaction.sepa["SVWZ"]
83-
8483
[
8584
remote_account,
8685
transaction.date,
8786
transaction.amount_in_cents,
8887
transaction.iban,
8988
transaction.name,
9089
transaction.sign,
91-
eref,
92-
mref,
93-
svwz,
90+
transaction.eref,
91+
transaction.mref,
92+
transaction.svwz,
9493
transaction.information.gsub(/\s/, "")
9594
]
9695
end
9796

9897
def self.statement_attributes_from_bank_transaction(transaction, bank_statement)
9998
{
100-
sha: checksum(transaction, bank_statement),
101-
date: transaction.date,
102-
entry_date: transaction.entry_date,
10399
amount: transaction.amount_in_cents,
104-
sign: transaction.sign,
105-
debit: transaction.debit?,
106-
swift_code: transaction.swift_code,
107-
reference: transaction.reference,
108100
bank_reference: transaction.bank_reference,
109101
bic: transaction.bic,
102+
creditor_identifier: transaction.creditor_identifier,
103+
date: transaction.date,
104+
debit: transaction.debit?,
105+
description: transaction.description,
106+
entry_date: transaction.entry_date,
107+
eref: transaction.eref,
110108
iban: transaction.iban,
111-
name: transaction.name,
112109
information: transaction.information,
113-
description: transaction.description,
114-
eref: transaction.respond_to?(:eref) ? transaction.eref : transaction.sepa["EREF"],
115-
mref: transaction.respond_to?(:mref) ? transaction.mref : transaction.sepa["MREF"],
116-
svwz: transaction.respond_to?(:svwz) ? transaction.svwz : transaction.sepa["SVWZ"],
117-
tx_id: transaction.try(:primanota) || transaction.try(:transaction_id),
118-
creditor_identifier: transaction.respond_to?(:creditor_identifier) ? transaction.creditor_identifier : transaction.sepa["CRED"]
110+
mref: transaction.mref,
111+
name: transaction.name,
112+
reference: transaction.reference,
113+
sha: checksum(transaction, bank_statement),
114+
sign: transaction.sign,
115+
svwz: transaction.svwz,
116+
swift_code: transaction.swift_code,
117+
tx_id: transaction.transaction_id,
119118
}
120119
end
121120
end

box/jobs/fetch_statements.rb

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
require "sidekiq-scheduler"
44
require "active_support/all"
5-
require "camt_parser"
5+
require "sepa_file_parser"
66
require "cmxl"
77
require "epics"
88
require "sequel"
@@ -84,7 +84,7 @@ def camt53(client, from, to)
8484
combined_camt = client.C53(from.to_s(:db), to.to_s(:db))
8585
return unless combined_camt.any?
8686

87-
combined_camt.map { |chunk| CamtParser::String.parse(chunk).statements }.flatten
87+
combined_camt.map { |chunk| SepaFileParser::String.parse(chunk).statements }.flatten
8888
end
8989

9090
def mt940(client, from, to)

box/jobs/fetch_upcoming_statements.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
require "sidekiq-scheduler"
44
require "active_support/all"
5-
require "camt_parser"
5+
require "sepa_file_parser"
66
require "cmxl"
77
require "epics"
88
require "sequel"

box/jobs/queue_fetch_statements.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
require "sidekiq-scheduler"
44
require "active_support/all"
5-
require "camt_parser"
5+
require "sepa_file_parser"
66
require "cmxl"
77
require "epics"
88
require "sequel"

box/jobs/queue_fetch_upcoming_statements.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
require "sidekiq-scheduler"
44
require "active_support/all"
5-
require "camt_parser"
5+
require "sepa_file_parser"
66
require "cmxl"
77
require "epics"
88
require "sequel"

lib/data_mapping/camt53/statement.rb

+8-4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
require_relative "transaction"
2+
13
module DataMapping
24
module Camt53
35
class Statement
@@ -12,27 +14,29 @@ def blank?
1214
end
1315

1416
def account_identification
15-
raw_bank_statement.account_identification
17+
raw_bank_statement.account
1618
end
1719

1820
def closing_or_intermediary_balance
19-
raw_bank_statement.closing_or_intermediary_balance
21+
raw_bank_statement.closing_balance
2022
end
2123

2224
def sequence
2325
raw_bank_statement.electronic_sequence_number
2426
end
2527

2628
def opening_or_intermediary_balance
27-
raw_bank_statement.closing_or_intermediary_balance
29+
raw_bank_statement.opening_balance
2830
end
2931

3032
def source
3133
raw_bank_statement.source
3234
end
3335

3436
def transactions
35-
raw_bank_statement.transactions
37+
raw_bank_statement.entries.map do |entry|
38+
Transaction.new(entry)
39+
end
3640
end
3741
end
3842
end
+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
module DataMapping
2+
module Camt53
3+
class Transaction
4+
attr_reader :raw_bank_statement, :first_transaction
5+
6+
delegate :amount_in_cents,
7+
:bank_reference,
8+
:credit?,
9+
:debit?,
10+
:reference,
11+
:sign,
12+
:transaction_id,
13+
to: :raw_bank_statement
14+
15+
def initialize(raw_bank_statement)
16+
@raw_bank_statement = raw_bank_statement
17+
@first_transaction = raw_bank_statement.transactions.first
18+
end
19+
20+
def bic
21+
first_transaction.bic
22+
end
23+
24+
def creditor_identifier
25+
first_transaction.creditor_identifier
26+
end
27+
28+
def date
29+
raw_bank_statement.value_date
30+
end
31+
32+
def description
33+
raw_bank_statement.additional_information
34+
end
35+
36+
def entry_date
37+
raw_bank_statement.booking_date
38+
end
39+
40+
def eref
41+
first_transaction.end_to_end_reference
42+
end
43+
44+
def iban
45+
first_transaction.iban
46+
end
47+
48+
def information
49+
first_transaction.payment_information
50+
end
51+
52+
def mref
53+
first_transaction.mandate_reference
54+
end
55+
56+
def name
57+
first_transaction.name
58+
end
59+
60+
def svwz
61+
first_transaction.remittance_information
62+
end
63+
64+
def swift_code
65+
first_transaction.swift_code
66+
end
67+
68+
def transaction_id
69+
first_transaction.transaction_id
70+
end
71+
end
72+
end
73+
end

lib/data_mapping/cmxl/account.rb

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
module DataMapping
2+
module Cmxl
3+
class Account
4+
attr_reader :raw_bank_statement
5+
6+
delegate :account_number,
7+
to: :raw_bank_statement
8+
9+
def initialize(raw_bank_statement)
10+
@raw_bank_statement = raw_bank_statement
11+
end
12+
13+
def iban
14+
return raw_bank_statement.iban if raw_bank_statement.iban.present?
15+
raw_bank_statement.source
16+
end
17+
end
18+
end
19+
end

lib/data_mapping/cmxl/statement.rb

+14-2
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
1+
require_relative "account"
2+
require_relative "transaction"
3+
14
module DataMapping
25
module Cmxl
36
class Statement
47
attr_reader :raw_bank_statement
58

6-
delegate :account_identification,
9+
delegate :account_number,
710
:blank?,
811
:closing_or_intermediary_balance,
912
:opening_or_intermediary_balance,
1013
:source,
11-
:transactions,
1214
to: :raw_bank_statement
1315

1416
def initialize(raw_bank_statement)
@@ -18,6 +20,16 @@ def initialize(raw_bank_statement)
1820
def sequence
1921
raw_bank_statement.legal_sequence_number
2022
end
23+
24+
def account_identification
25+
Account.new(raw_bank_statement.account_identification)
26+
end
27+
28+
def transactions
29+
raw_bank_statement.transactions.map do |entry|
30+
Transaction.new(entry)
31+
end
32+
end
2133
end
2234
end
2335
end

0 commit comments

Comments
 (0)