cmxl 1.4.6 → 2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +30 -0
- data/.github/workflows/release.yml +26 -0
- data/.rspec +1 -1
- data/CHANGELOG.mdown +13 -0
- data/README.md +17 -17
- data/cmxl.gemspec +2 -2
- data/lib/cmxl/field.rb +1 -1
- data/lib/cmxl/fields/transaction.rb +43 -8
- data/lib/cmxl/statement.rb +2 -1
- data/lib/cmxl/version.rb +1 -1
- data/lib/cmxl.rb +5 -5
- data/spec/fields/transaction_spec.rb +98 -4
- data/spec/fixtures/lines/statement_line.txt +2 -0
- data/spec/fixtures/mt940.txt +31 -0
- data/spec/fixtures/mt942.txt +12 -0
- data/spec/mt940_parsing_spec.rb +8 -1
- data/spec/statement_spec.rb +23 -1
- metadata +19 -20
- data/.semaphore/semaphore.yml +0 -101
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1c172d6b56767a8860f57c4b802c7ed2ed2bae3658f6db104f44795a377bf067
|
4
|
+
data.tar.gz: ececb262e0d6a40c97e7ca54dcac08208e1942792c5f493be179ba3c81ece6b7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f0f673453d6bbbc39313ed7ae86ff574c4c9d60a0ea5543283f38d1f3bf4f7477625e2bc87a1fbdc62714c86461735a7e16796edd54cd361d52d49d2eb05d8db
|
7
|
+
data.tar.gz: a197094a3ccd4235f8811a456dbd54c43bc3b49a47062a72f7b9ee53610ddbb4e79ce97e53e0f90670a0c5e3cb5f08ef490792506dc6cc1202d5442c4f724710
|
@@ -0,0 +1,30 @@
|
|
1
|
+
name: CI
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches: [master]
|
6
|
+
pull_request:
|
7
|
+
branches: [master]
|
8
|
+
|
9
|
+
jobs:
|
10
|
+
test:
|
11
|
+
runs-on: ubuntu-latest
|
12
|
+
|
13
|
+
strategy:
|
14
|
+
matrix:
|
15
|
+
ruby-version:
|
16
|
+
- 3.3
|
17
|
+
- 3.2
|
18
|
+
- 3.1
|
19
|
+
- "3.0"
|
20
|
+
- jruby-9.4
|
21
|
+
|
22
|
+
steps:
|
23
|
+
- uses: actions/checkout@v4
|
24
|
+
- name: Set up Ruby ${{ matrix.ruby-version }}
|
25
|
+
uses: ruby/setup-ruby@v1
|
26
|
+
with:
|
27
|
+
ruby-version: ${{ matrix.ruby-version }}
|
28
|
+
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
29
|
+
- name: Run tests
|
30
|
+
run: bundle exec rake
|
@@ -0,0 +1,26 @@
|
|
1
|
+
name: Release
|
2
|
+
|
3
|
+
on:
|
4
|
+
release:
|
5
|
+
types: [published]
|
6
|
+
|
7
|
+
jobs:
|
8
|
+
push:
|
9
|
+
name: Push gem to RubyGems.org
|
10
|
+
runs-on: ubuntu-latest
|
11
|
+
|
12
|
+
permissions:
|
13
|
+
id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
|
14
|
+
contents: write # IMPORTANT: this permission is required for `rake release` to push the release tag
|
15
|
+
|
16
|
+
steps:
|
17
|
+
# Set up
|
18
|
+
- uses: actions/checkout@v4
|
19
|
+
- name: Set up Ruby
|
20
|
+
uses: ruby/setup-ruby@v1
|
21
|
+
with:
|
22
|
+
bundler-cache: true
|
23
|
+
ruby-version: ruby
|
24
|
+
|
25
|
+
# Release
|
26
|
+
- uses: rubygems/release-gem@v1
|
data/.rspec
CHANGED
@@ -1 +1 @@
|
|
1
|
-
--color
|
1
|
+
--color
|
data/CHANGELOG.mdown
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
+
# NEXT release
|
2
|
+
|
3
|
+
# 2.0
|
4
|
+
- `[REFACTOR]` **DEPRECATED:** `storno?` and related methods `storno_credit`, `storno_debit`. Use `reversal?` and related methods `reversal_credit?`, `reversal_debit?` instead
|
5
|
+
- `[BUGFIX]` **DEPRECATED:** `funds_code` method returns the `credit_debit_indicator` from the SWIFT definition. Therefore the method is deprecated in favor of `credit_debit_indicator` method
|
6
|
+
- `[HOUSEKEEPING]` [Replace Travis CI with github actions](https://github.com/railslove/cmxl/pull/57)
|
7
|
+
|
8
|
+
# 1.5.0
|
9
|
+
|
10
|
+
- `[BUGFIX]` fix potential bug when generation_date is not provided in field 20 and 13 (issue: [#35](https://github.com/railslove/cmxl/issues/35) PR: [#36](https://github.com/railslove/cmxl/pull/36))
|
11
|
+
- `[FEATURE]` add transaction type 'S' to Field 61 (swift_code) (issue: [#38](https://github.com/railslove/cmxl/issues/38) PR: [#42](https://github.com/railslove/cmxl/pull/42)
|
12
|
+
- `[ENHANCEMENT]` [Fix deprecations in ruby 2.7 for kw params](https://github.com/railslove/cmxl/commit/905cd50e5372da312a653c89e77727a14751b5ec)
|
13
|
+
|
1
14
|
# 1.4.6
|
2
15
|
|
3
16
|
- `[ENHANCEMENT]` make sure that tag, gemversion and readme align ʘ‿ʘ
|
data/README.md
CHANGED
@@ -1,14 +1,12 @@
|
|
1
|
-
[](https://codeclimate.com/github/railslove/cmxl)
|
4
|
-
[](http://badge.fury.io/rb/cmxl)
|
1
|
+
[](https://github.com/railslove/cmxl/actions/workflows/ci.yml)
|
2
|
+
[](https://rubygems.org/gems/cmxl)
|
5
3
|
|
6
4
|
# Cmxl - your friendly ruby MT940 parser
|
7
5
|
|
8
6
|
At [Railslove](http://railslove.com) we build a lot of financial applications and work on integrating applications with banks and banking functionality.
|
9
7
|
Our goal is to make simple solutions for what often looks complicated.
|
10
8
|
|
11
|
-
Cmxl is a friendly and
|
9
|
+
Cmxl is a friendly and extensible MT940 bank statement file parser that helps you extract data from bank statement files.
|
12
10
|
|
13
11
|
## What is MT940 & MT942?
|
14
12
|
|
@@ -16,13 +14,13 @@ MT940 (MT = Message Type) is the SWIFT-Standard for the electronic transfer of b
|
|
16
14
|
When integrating with banks you often get MT940 or MT942 files as interface.
|
17
15
|
For more information have a look at the different [SWIFT message types](http://en.wikipedia.org/wiki/SWIFT_message_types)
|
18
16
|
|
19
|
-
At some point in the future MT940 file should be exchanged with newer XML documents - but banking institutions are slow so MT940 will stick around for a while.
|
17
|
+
At some point in the future MT940 file should be exchanged with newer XML documents - but banking institutions are slow, so MT940 will stick around for a while.
|
20
18
|
|
21
|
-
##
|
19
|
+
## Requirements
|
22
20
|
|
23
21
|
Cmxl is a pure ruby parser and has no dependency on native extensions.
|
24
22
|
|
25
|
-
- Ruby
|
23
|
+
- Ruby (current officially supported distributions)
|
26
24
|
|
27
25
|
## Installation
|
28
26
|
|
@@ -52,7 +50,7 @@ Cmxl.config[:statement_separator] = /\n-.\n/m
|
|
52
50
|
# do you want an error to be raised when a line can not be parsed? default is true
|
53
51
|
Cmxl.config[:raise_line_format_errors] = true
|
54
52
|
|
55
|
-
# try to stip the SWIFT header data. This strips everything until the actual first MT940 field. (if parsing fails try this!)
|
53
|
+
# try to stip the SWIFT header data. This strips everything until the actual first MT940 field. (if parsing fails, try this!)
|
56
54
|
Cmxl.config[:strip_headers] = true
|
57
55
|
|
58
56
|
|
@@ -70,7 +68,7 @@ statements.each do |s|
|
|
70
68
|
puts t.information
|
71
69
|
puts t.description
|
72
70
|
puts t.entry_date
|
73
|
-
puts t.
|
71
|
+
puts t.credit_debit_indicator
|
74
72
|
puts t.credit?
|
75
73
|
puts t.debit?
|
76
74
|
puts t.sign # -1 if it's a debit; 1 if it's a credit
|
@@ -86,19 +84,19 @@ end
|
|
86
84
|
|
87
85
|
```
|
88
86
|
|
89
|
-
Every object responds to `to_h` and let's you easily convert the data to a hash. Also every object responds to `to_json` which lets you easily represent the statements as JSON with your
|
87
|
+
Every object responds to `to_h` and let's you easily convert the data to a hash. Also every object responds to `to_json` which lets you easily represent the statements as JSON with your favorite JSON library.
|
90
88
|
|
91
|
-
#### A note about encoding and file
|
89
|
+
#### A note about encoding and file weirdnesses
|
92
90
|
|
93
91
|
You probably will encounter encoding issues (hey, you are building banking applications!).
|
94
|
-
We try to handle encoding and format
|
95
|
-
In the likely case that you
|
96
|
-
If that fails try to
|
92
|
+
We try to handle encoding and format weirdnesses as much as possible. If no encoding is passed we try to guess the encoding of the data and convert it to UTF8.
|
93
|
+
In the likely case that you encounter encoding issues you can pass encoding options to `Cmxl.parse(<string>, <options hash>)`. It accepts the same options as [String#encode](http://ruby-doc.org/core-2.1.3/String.html#method-i-encode)
|
94
|
+
If that fails, try to modify the file before you pass it to the parser - and please create an issue.
|
97
95
|
|
98
96
|
### MT940 SWIFT header data
|
99
97
|
|
100
98
|
Cmxl currently does not support parsing of the SWIFT headers (like {1:F01AXISINBBA ....)
|
101
|
-
If your file comes with these headers try the `strip_headers` configuration option to strip data
|
99
|
+
If your file comes with these headers try the `strip_headers` configuration option to strip data except the actual MT940 fields.
|
102
100
|
|
103
101
|
```ruby
|
104
102
|
Cmxl.config[:strip_headers] = true
|
@@ -188,5 +186,7 @@ other parsers:
|
|
188
186
|
- [gmitrev/mt940parser](https://github.com/gmitrev/mt940parser)
|
189
187
|
|
190
188
|
---
|
189
|
+
built with love by [Railslove](http://railslove.com) and some [amazing people](https://github.com/railslove/cmxl/graphs/contributors).
|
190
|
+
Released under the MIT-License.
|
191
191
|
|
192
|
-
|
192
|
+
Railslove builds FinTech products, if you need support for your project we are happy to help. Please contact us at team@railslove.com.
|
data/cmxl.gemspec
CHANGED
@@ -19,11 +19,11 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
20
20
|
spec.require_paths = ['lib']
|
21
21
|
|
22
|
+
spec.add_dependency 'rchardet'
|
23
|
+
|
22
24
|
spec.add_development_dependency 'bundler'
|
23
25
|
spec.add_development_dependency 'pry'
|
24
26
|
spec.add_development_dependency 'rake'
|
25
27
|
spec.add_development_dependency 'rspec', '~>3.0'
|
26
28
|
spec.add_development_dependency 'simplecov'
|
27
|
-
|
28
|
-
spec.add_dependency 'rchardet19'
|
29
29
|
end
|
data/lib/cmxl/field.rb
CHANGED
@@ -13,7 +13,7 @@ module Cmxl
|
|
13
13
|
|
14
14
|
# The parser class variable is the registry of all available parser.
|
15
15
|
# It is a hash with the tag (MT940 field number/tag) as key and the class as value
|
16
|
-
# When parsing a
|
16
|
+
# When parsing a statement line we look for a matching entry or use the Unknown class as default
|
17
17
|
@@parsers = {}
|
18
18
|
@@parsers.default = Unknown
|
19
19
|
def self.parsers
|
@@ -2,7 +2,7 @@ module Cmxl
|
|
2
2
|
module Fields
|
3
3
|
class Transaction < Field
|
4
4
|
self.tag = 61
|
5
|
-
self.parser = %r{^(?<date>\d{6})(?<entry_date>\d{4})?(?<
|
5
|
+
self.parser = %r{^(?<date>\d{6})(?<entry_date>\d{4})?(?<credit_debit_indicator>D|C|RD|RC|ED|EC)(?<currency_letter>[a-zA-Z])?(?<amount>\d{1,12},\d{0,2})(?<swift_code>(?:N|F|S).{3})(?<reference>NONREF|(.(?!\/\/)){,16}([^\/]){,1})((?:\/\/)(?<bank_reference>[^\n]{,16}))?((?:\n)(?<supplementary>.{,34}))?$}
|
6
6
|
|
7
7
|
attr_accessor :details
|
8
8
|
|
@@ -15,31 +15,63 @@ module Cmxl
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def credit?
|
18
|
-
|
18
|
+
credit_debit_indicator.include?('C')
|
19
19
|
end
|
20
20
|
|
21
21
|
def debit?
|
22
|
-
|
22
|
+
credit_debit_indicator.include?('D')
|
23
23
|
end
|
24
24
|
|
25
25
|
def storno_credit?
|
26
|
+
warn "[DEPRECATION] `storno_credit?` is deprecated. Please use `reversal_credit?` instead. It will be removed in version 3.0."
|
27
|
+
reversal_credit?
|
28
|
+
end
|
29
|
+
|
30
|
+
def reversal_credit?
|
26
31
|
credit? && storno?
|
27
32
|
end
|
28
33
|
|
29
34
|
def storno_debit?
|
35
|
+
warn "[DEPRECATION] `storno_debit?` is deprecated. Please use `reversal_debit?` instead. It will be removed in version 3.0."
|
36
|
+
reversal_debit?
|
37
|
+
end
|
38
|
+
|
39
|
+
def reversal_debit?
|
30
40
|
debit? && storno?
|
31
41
|
end
|
32
42
|
|
33
43
|
def storno?
|
34
|
-
|
44
|
+
warn "[DEPRECATION] `storno?` is deprecated. Please use `reversal?` instead. It will be removed in version 3.0."
|
45
|
+
reversal?
|
46
|
+
end
|
47
|
+
|
48
|
+
def reversal?
|
49
|
+
credit_debit_indicator.include?('R')
|
50
|
+
end
|
51
|
+
|
52
|
+
def expected_credit?
|
53
|
+
credit? && expected?
|
54
|
+
end
|
55
|
+
|
56
|
+
def expected_debit?
|
57
|
+
debit? && expected?
|
58
|
+
end
|
59
|
+
|
60
|
+
def expected?
|
61
|
+
credit_debit_indicator.include?('E')
|
62
|
+
end
|
63
|
+
|
64
|
+
def credit_debit_indicator
|
65
|
+
data['credit_debit_indicator'].to_s
|
35
66
|
end
|
36
67
|
|
37
68
|
def funds_code
|
38
|
-
|
69
|
+
warn "[DEPRECATION] `funds_code` is deprecated. Please use `credit_debit_indicator` instead. It will be removed in version 3.0."
|
70
|
+
data['credit_debit_indicator'].to_s
|
39
71
|
end
|
40
72
|
|
41
73
|
def storno_flag
|
42
|
-
|
74
|
+
reversal? ? 'R' : ''
|
43
75
|
end
|
44
76
|
|
45
77
|
def sign
|
@@ -140,8 +172,11 @@ module Cmxl
|
|
140
172
|
'sign' => sign,
|
141
173
|
'debit' => debit?,
|
142
174
|
'credit' => credit?,
|
143
|
-
'storno' =>
|
144
|
-
'
|
175
|
+
'storno' => reversal?,
|
176
|
+
'reversal' => reversal?,
|
177
|
+
'expected' => expected?,
|
178
|
+
'funds_code' => credit_debit_indicator,
|
179
|
+
'credit_debit_indicator' => credit_debit_indicator,
|
145
180
|
'swift_code' => swift_code,
|
146
181
|
'reference' => reference,
|
147
182
|
'bank_reference' => bank_reference,
|
data/lib/cmxl/statement.rb
CHANGED
@@ -58,8 +58,9 @@ module Cmxl
|
|
58
58
|
field(20).reference
|
59
59
|
end
|
60
60
|
|
61
|
+
#Get generation date from field 20. If generation date is not provided in field 20, method will fall back to field 13 if present.
|
61
62
|
def generation_date
|
62
|
-
field(20).date || field(13).date
|
63
|
+
field(20).date || (field(13).nil? ? nil : field(13).date)
|
63
64
|
end
|
64
65
|
|
65
66
|
def account_identification
|
data/lib/cmxl/version.rb
CHANGED
data/lib/cmxl.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'cmxl/version'
|
2
2
|
|
3
|
-
require '
|
3
|
+
require 'rchardet'
|
4
4
|
|
5
5
|
require 'cmxl/field'
|
6
6
|
require 'cmxl/statement'
|
@@ -31,14 +31,14 @@ module Cmxl
|
|
31
31
|
def self.parse(data, options = {})
|
32
32
|
options[:statement_separator] ||= config[:statement_separator]
|
33
33
|
# if no encoding is provided we try to guess using CharDet
|
34
|
-
if options[:encoding].nil? && cd = CharDet.detect(data
|
35
|
-
options[:encoding] = cd
|
34
|
+
if options[:encoding].nil? && cd = CharDet.detect(data)
|
35
|
+
options[:encoding] = cd['encoding']
|
36
36
|
end
|
37
37
|
|
38
38
|
if options[:encoding]
|
39
|
-
data.encode!('UTF-8', options.delete(:encoding), options)
|
39
|
+
data.encode!('UTF-8', options.delete(:encoding), **options)
|
40
40
|
else
|
41
|
-
data.encode!('UTF-8', options) unless options.empty?
|
41
|
+
data.encode!('UTF-8', **options) unless options.empty?
|
42
42
|
end
|
43
43
|
|
44
44
|
data.split(options[:statement_separator]).reject { |s| s.strip.empty? }.collect { |s| Cmxl::Statement.new(s.strip) }
|
@@ -7,8 +7,9 @@ describe Cmxl::Fields::Transaction do
|
|
7
7
|
subject(:ocmt_cghs_transaction) { Cmxl::Fields::Transaction.parse(fixture_line(:statement_ocmt_chgs)) }
|
8
8
|
subject(:supplementary_transaction) { Cmxl::Fields::Transaction.parse(fixture_line(:statement_supplementary_plain)) }
|
9
9
|
subject(:complex_supplementary_transaction) { Cmxl::Fields::Transaction.parse(fixture_line(:statement_supplementary_complex)) }
|
10
|
-
subject(:
|
10
|
+
subject(:valuta_after_entry_date) { Cmxl::Fields::Transaction.parse(fixture[3]) }
|
11
11
|
subject(:entry_before_valuta_transaction) { Cmxl::Fields::Transaction.parse(fixture[2]) }
|
12
|
+
subject(:transaction_type_swift) { Cmxl::Fields::Transaction.parse(fixture[4]) }
|
12
13
|
|
13
14
|
let(:fixture) { fixture_line(:statement_line).split(/\n/) }
|
14
15
|
|
@@ -69,6 +70,11 @@ describe Cmxl::Fields::Transaction do
|
|
69
70
|
end
|
70
71
|
|
71
72
|
context 'statement with complex supplementary' do
|
73
|
+
it 'future reference' do
|
74
|
+
result = Cmxl::Fields::Transaction.parse(':61:2412121212ED162,57NDDTNONREF//950\n')
|
75
|
+
expect(result.amount).to eql(162.57)
|
76
|
+
end
|
77
|
+
|
72
78
|
it { expect(complex_supplementary_transaction.initial_amount_in_cents).to eql(nil) }
|
73
79
|
it { expect(complex_supplementary_transaction.initial_currency).to eql(nil) }
|
74
80
|
|
@@ -81,14 +87,102 @@ describe Cmxl::Fields::Transaction do
|
|
81
87
|
end
|
82
88
|
|
83
89
|
context 'valuta and entry-date assumptions' do
|
84
|
-
it 'entry_date before valuta is recognized
|
90
|
+
it 'entry_date before valuta is recognized correctly when including year-change' do
|
85
91
|
expect(entry_before_valuta_transaction.date).to eql(Date.new(2014, 1, 10))
|
86
92
|
expect(entry_before_valuta_transaction.entry_date).to eql(Date.new(2013, 12, 24))
|
87
93
|
end
|
88
94
|
|
89
95
|
it 'entry_date after valuta is recognized correctly when including year-change' do
|
90
|
-
expect(
|
91
|
-
expect(
|
96
|
+
expect(valuta_after_entry_date.date).to eql(Date.new(2014, 12, 24))
|
97
|
+
expect(valuta_after_entry_date.entry_date).to eql(Date.new(2015, 1, 2))
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
context 'transaction type swift transfer' do
|
102
|
+
it { expect(transaction_type_swift.date).to eql(Date.new(2019, 11, 18)) }
|
103
|
+
it { expect(transaction_type_swift.entry_date).to eql(Date.new(2019, 11, 18)) }
|
104
|
+
it { expect(transaction_type_swift.funds_code).to eql('C') }
|
105
|
+
it { expect(transaction_type_swift.currency_letter).to eql('R') }
|
106
|
+
it { expect(transaction_type_swift.amount).to eql(653.0) }
|
107
|
+
it { expect(transaction_type_swift.amount_in_cents).to eql(65300) }
|
108
|
+
it { expect(transaction_type_swift.swift_code).to eql('S445') }
|
109
|
+
it { expect(transaction_type_swift.reference).to eql('328556-76501096') }
|
110
|
+
it { expect(transaction_type_swift).to be_credit }
|
111
|
+
it { expect(transaction_type_swift).not_to be_debit }
|
112
|
+
it { expect(transaction_type_swift).not_to be_storno }
|
113
|
+
it { expect(transaction_type_swift.sign).to eql(1) }
|
114
|
+
end
|
115
|
+
|
116
|
+
describe '#credit_debit_indicator' do
|
117
|
+
it 'returns the credit_debit_indicator as debit' do
|
118
|
+
result = Cmxl::Fields::Transaction.parse(':61:1409010902DR000000000001,62NTRF0000549855700010//025498557/000001')
|
119
|
+
expect(result.credit_debit_indicator).to eql('D')
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'returns the credit_debit_indicator as credit' do
|
123
|
+
result = Cmxl::Fields::Transaction.parse(':61:1409010902CR000000000001,62NTRF0000549855700010//025498557/000001')
|
124
|
+
expect(result.credit_debit_indicator).to eql('C')
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'returns the credit_debit_indicator as reversal credit' do
|
128
|
+
result = Cmxl::Fields::Transaction.parse(':61:1409010902RC000000000001,62NTRF0000549855700010//025498557/000001')
|
129
|
+
expect(result.credit_debit_indicator).to eql('RC')
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'returns the credit_debit_indicator as reversal debit' do
|
133
|
+
result = Cmxl::Fields::Transaction.parse(':61:1409010902RD000000000001,62NTRF0000549855700010//025498557/000001')
|
134
|
+
expect(result.credit_debit_indicator).to eql('RD')
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'returns the credit_debit_indicator as expected credit' do
|
138
|
+
result = Cmxl::Fields::Transaction.parse(':61:1409010902EC000000000001,62NTRF0000549855700010//025498557/000001')
|
139
|
+
expect(result.credit_debit_indicator).to eql('EC')
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
describe '#expected?' do
|
144
|
+
it 'returns true if the transaction is expected' do
|
145
|
+
result = Cmxl::Fields::Transaction.parse(':61:1409010902EC000000000001,62NTRF0000549855700010//025498557/000001')
|
146
|
+
expect(result).to be_expected
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'returns false if the transaction is not expected' do
|
150
|
+
result = Cmxl::Fields::Transaction.parse(':61:1409010902RD000000000001,62NTRF0000549855700010//025498557/000001')
|
151
|
+
expect(result).not_to be_expected
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
describe '#expected_credit?' do
|
156
|
+
it 'returns true if the transaction is expected and credit' do
|
157
|
+
result = Cmxl::Fields::Transaction.parse(':61:1409010902EC000000000001,62NTRF0000549855700010//025498557/000001')
|
158
|
+
expect(result).to be_expected_credit
|
159
|
+
end
|
160
|
+
|
161
|
+
it 'returns false if the transaction is not expected and credit' do
|
162
|
+
result = Cmxl::Fields::Transaction.parse(':61:1409010902RC000000000001,62NTRF0000549855700010//025498557/000001')
|
163
|
+
expect(result).not_to be_expected_credit
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'returns false if the transaction is expected and debit' do
|
167
|
+
result = Cmxl::Fields::Transaction.parse(':61:1409010902ED000000000001,62NTRF0000549855700010//025498557/000001')
|
168
|
+
expect(result).not_to be_expected_credit
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
describe '#expected_debit?' do
|
173
|
+
it 'returns true if the transaction is expected and debit' do
|
174
|
+
result = Cmxl::Fields::Transaction.parse(':61:1409010902ED000000000001,62NTRF0000549855700010//025498557/000001')
|
175
|
+
expect(result).to be_expected_debit
|
176
|
+
end
|
177
|
+
|
178
|
+
it 'returns false if the transaction is not expected and debit' do
|
179
|
+
result = Cmxl::Fields::Transaction.parse(':61:1409010902RD000000000001,62NTRF0000549855700010//025498557/000001')
|
180
|
+
expect(result).not_to be_expected_debit
|
181
|
+
end
|
182
|
+
|
183
|
+
it 'returns false if the transaction is expected and credit' do
|
184
|
+
result = Cmxl::Fields::Transaction.parse(':61:1409010902EC000000000001,62NTRF0000549855700010//025498557/000001')
|
185
|
+
expect(result).not_to be_expected_debit
|
92
186
|
end
|
93
187
|
end
|
94
188
|
end
|
data/spec/fixtures/mt940.txt
CHANGED
@@ -74,3 +74,34 @@ Interest credit
|
|
74
74
|
:62F:C020325PLN50040,00
|
75
75
|
|
76
76
|
-
|
77
|
+
|
78
|
+
:20:01ANWZTPJTYEGJWA
|
79
|
+
:25:45050050/76198810
|
80
|
+
:28:27/01
|
81
|
+
:60F:C131016DEM84349,74
|
82
|
+
:61:131017D6800,NCHK16703074
|
83
|
+
:86:999PN5477SCHECK-NR. 0000016703074
|
84
|
+
:61:131017D620,3NSTON
|
85
|
+
:86:999PN0911DAUERAUFTR.NR. 14
|
86
|
+
:61:131017C18500,NCLRN
|
87
|
+
:86:999PN2406SCHECK
|
88
|
+
:61:131015D14220,NBOEN
|
89
|
+
:86:999PN0920WECHSEL
|
90
|
+
:61:131017D1507,NTRFN
|
91
|
+
:86:999PN0920SCHNELLUEB
|
92
|
+
:61:131024C4200,NMSCN
|
93
|
+
:86:999PN2506AUSSENH. NR. 1
|
94
|
+
:61:131017D19900,NTRFN
|
95
|
+
:86:999PN0907UEBERTRAG
|
96
|
+
:61:131017D400,NTRFN
|
97
|
+
:86:999PN0891BTX
|
98
|
+
:61:131018C3656,74NMSCN
|
99
|
+
:86:999PN0850EINZAHLG.N
|
100
|
+
:61:131019C23040,NMSCN
|
101
|
+
:86:999PN0812LT.ANLAGE
|
102
|
+
:61:131027D5862,14NCHKN
|
103
|
+
:86:999PN5329AUSLSCHECK
|
104
|
+
:62F:C131017DEM84437,04
|
105
|
+
|
106
|
+
-
|
107
|
+
|
data/spec/fixtures/mt942.txt
CHANGED
@@ -7,3 +7,15 @@
|
|
7
7
|
:86:008?00DAUERAUFTRAG?100599?20Miete Dezember?3010020030?31234567?32MUELLER?34339
|
8
8
|
:90D:0EUR0,00
|
9
9
|
:90C:1EUR9792,00
|
10
|
+
|
11
|
+
-
|
12
|
+
|
13
|
+
:20:01ANWZTPJTYEGJWA
|
14
|
+
:25:45050050/76198810
|
15
|
+
:28C:13/37
|
16
|
+
:34F:EURD12345,67
|
17
|
+
:13D:1901091733+0100
|
18
|
+
:61:190109C9792,00FTRFNONREF
|
19
|
+
:86:008?00DAUERAUFTRAG?100599?20Miete Dezember?3010020030?31234567?32MUELLER?34339
|
20
|
+
:90D:0EUR0,00
|
21
|
+
:90C:1EUR9792,00
|
data/spec/mt940_parsing_spec.rb
CHANGED
@@ -61,11 +61,18 @@ describe 'parsing a statement' do
|
|
61
61
|
it { expect(subject.field('NS').to_h).to eql({tag: 'NS', modifier: 'F', source: 'HelloWorld'}) }
|
62
62
|
end
|
63
63
|
|
64
|
+
context 'no generation date' do
|
65
|
+
subject { Cmxl.parse(mt940_file('mt940'))[3] }
|
66
|
+
|
67
|
+
it { expect(subject.reference).to eql('01ANWZTPJTYEGJWA') }
|
68
|
+
it { expect(subject.generation_date).to eql(nil) }
|
69
|
+
end
|
70
|
+
|
64
71
|
context 'statement separator as used by most banks' do
|
65
72
|
subject { Cmxl.parse(mt940_file('mt940')) }
|
66
73
|
|
67
74
|
it 'detects all statements by default' do
|
68
|
-
expect(subject.size).to eq(
|
75
|
+
expect(subject.size).to eq(4)
|
69
76
|
end
|
70
77
|
end
|
71
78
|
|
data/spec/statement_spec.rb
CHANGED
@@ -27,9 +27,12 @@ describe Cmxl do
|
|
27
27
|
'bank_reference' => '025498557/000001',
|
28
28
|
'amount_in_cents' => 162,
|
29
29
|
'sign' => -1,
|
30
|
+
'credit_debit_indicator' => 'D',
|
30
31
|
'debit' => true,
|
31
32
|
'credit' => false,
|
32
33
|
'storno' => false,
|
34
|
+
'reversal' => false,
|
35
|
+
'expected' => false,
|
33
36
|
'bic' => 'HYVEDEMMXXX',
|
34
37
|
'iban' => 'HUkkbbbsssskcccccccccccccccx',
|
35
38
|
'name' => 'Peter Pan',
|
@@ -81,6 +84,7 @@ describe Cmxl do
|
|
81
84
|
'sha' => '3c5e65aa3d3878b06b58b6f1ae2f3693004dfb04e3ab7119a1c1244e612293da',
|
82
85
|
'entry_date' => Date.new(2014, 9, 2),
|
83
86
|
'funds_code' => 'D',
|
87
|
+
'credit_debit_indicator' => 'D',
|
84
88
|
'currency_letter' => 'R',
|
85
89
|
'amount' => 1.62,
|
86
90
|
'swift_code' => 'NTRF',
|
@@ -90,7 +94,9 @@ describe Cmxl do
|
|
90
94
|
'sign' => -1,
|
91
95
|
'debit' => true,
|
92
96
|
'credit' => false,
|
93
|
-
'storno' => false
|
97
|
+
'storno' => false,
|
98
|
+
'reversal' => false,
|
99
|
+
'expected' => false,
|
94
100
|
)
|
95
101
|
end
|
96
102
|
end
|
@@ -145,4 +151,20 @@ describe Cmxl do
|
|
145
151
|
expect(subject.transactions.first.iban).to eq('234567')
|
146
152
|
end
|
147
153
|
end
|
154
|
+
|
155
|
+
context 'mt942 generation date from field 20' do
|
156
|
+
subject(:statement) { Cmxl.parse(mt940_file('mt942')).first }
|
157
|
+
|
158
|
+
it { expect(statement.mt942?).to be_truthy }
|
159
|
+
it { expect(statement.generation_date).to eql(Date.new(2013, 11, 10)) }
|
160
|
+
|
161
|
+
end
|
162
|
+
|
163
|
+
context 'mt942 generation date from field 13' do
|
164
|
+
subject(:statement) { Cmxl.parse(mt940_file('mt942'))[1] }
|
165
|
+
|
166
|
+
it { expect(statement.mt942?).to be_truthy }
|
167
|
+
it { expect(statement.generation_date).to eql(Date.new(2019, 1, 9)) }
|
168
|
+
end
|
169
|
+
|
148
170
|
end
|
metadata
CHANGED
@@ -1,15 +1,28 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cmxl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: '2.0'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Bumann
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 2025-01-10 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: rchardet
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - ">="
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '0'
|
19
|
+
type: :runtime
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - ">="
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: '0'
|
13
26
|
- !ruby/object:Gem::Dependency
|
14
27
|
name: bundler
|
15
28
|
requirement: !ruby/object:Gem::Requirement
|
@@ -80,20 +93,6 @@ dependencies:
|
|
80
93
|
- - ">="
|
81
94
|
- !ruby/object:Gem::Version
|
82
95
|
version: '0'
|
83
|
-
- !ruby/object:Gem::Dependency
|
84
|
-
name: rchardet19
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
86
|
-
requirements:
|
87
|
-
- - ">="
|
88
|
-
- !ruby/object:Gem::Version
|
89
|
-
version: '0'
|
90
|
-
type: :runtime
|
91
|
-
prerelease: false
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
93
|
-
requirements:
|
94
|
-
- - ">="
|
95
|
-
- !ruby/object:Gem::Version
|
96
|
-
version: '0'
|
97
96
|
description: Cmxl provides an friendly, extensible and customizable parser for the
|
98
97
|
MT940 bank statement format.
|
99
98
|
email:
|
@@ -102,9 +101,10 @@ executables: []
|
|
102
101
|
extensions: []
|
103
102
|
extra_rdoc_files: []
|
104
103
|
files:
|
104
|
+
- ".github/workflows/ci.yml"
|
105
|
+
- ".github/workflows/release.yml"
|
105
106
|
- ".gitignore"
|
106
107
|
- ".rspec"
|
107
|
-
- ".semaphore/semaphore.yml"
|
108
108
|
- CHANGELOG.mdown
|
109
109
|
- Gemfile
|
110
110
|
- LICENSE.txt
|
@@ -198,8 +198,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
198
198
|
- !ruby/object:Gem::Version
|
199
199
|
version: '0'
|
200
200
|
requirements: []
|
201
|
-
rubygems_version: 3.
|
202
|
-
signing_key:
|
201
|
+
rubygems_version: 3.6.2
|
203
202
|
specification_version: 4
|
204
203
|
summary: Cmxl is your friendly MT940 bank statement parser
|
205
204
|
test_files:
|
data/.semaphore/semaphore.yml
DELETED
@@ -1,101 +0,0 @@
|
|
1
|
-
version: v1.0
|
2
|
-
name: Test
|
3
|
-
agent:
|
4
|
-
machine:
|
5
|
-
type: e1-standard-2
|
6
|
-
os_image: ubuntu1804
|
7
|
-
blocks:
|
8
|
-
- name: Setup
|
9
|
-
task:
|
10
|
-
jobs:
|
11
|
-
- name: Checkout
|
12
|
-
commands:
|
13
|
-
- checkout
|
14
|
-
- ls
|
15
|
-
- rm -f .rbenv-version .ruby-version
|
16
|
-
|
17
|
-
- name: Tests
|
18
|
-
task:
|
19
|
-
env_vars:
|
20
|
-
- name: CC_TEST_REPORTER_ID
|
21
|
-
value: 149f0d20e17ace00c44be432a4194bc441ba805da89a7708fca0f4a2c3f2aed7
|
22
|
-
jobs:
|
23
|
-
- name: "2.0.0-p648"
|
24
|
-
commands:
|
25
|
-
- checkout
|
26
|
-
- sem-version ruby 2.0.0-p648
|
27
|
-
- cache restore
|
28
|
-
- bundle install
|
29
|
-
- cache store
|
30
|
-
- bundle exec rspec
|
31
|
-
- name: "2.1.10"
|
32
|
-
commands:
|
33
|
-
- checkout
|
34
|
-
- sem-version ruby 2.1.10
|
35
|
-
- cache restore
|
36
|
-
- bundle install
|
37
|
-
- cache store
|
38
|
-
- bundle exec rspec
|
39
|
-
- name: "2.2.10"
|
40
|
-
commands:
|
41
|
-
- checkout
|
42
|
-
- sem-version ruby 2.2.10
|
43
|
-
- cache restore
|
44
|
-
- bundle install
|
45
|
-
- cache store
|
46
|
-
- bundle exec rspec
|
47
|
-
- name: "2.3"
|
48
|
-
commands:
|
49
|
-
- checkout
|
50
|
-
- sem-version ruby 2.3
|
51
|
-
- cache restore
|
52
|
-
- bundle install
|
53
|
-
- cache store
|
54
|
-
- bundle exec rspec
|
55
|
-
- name: "2.4"
|
56
|
-
commands:
|
57
|
-
- checkout
|
58
|
-
- sem-version ruby 2.4
|
59
|
-
- cache restore
|
60
|
-
- bundle install
|
61
|
-
- cache store
|
62
|
-
- bundle exec rspec
|
63
|
-
- name: "2.5"
|
64
|
-
commands:
|
65
|
-
- checkout
|
66
|
-
- sem-version ruby 2.5
|
67
|
-
- cache restore
|
68
|
-
- bundle install
|
69
|
-
- cache store
|
70
|
-
- bundle exec rspec
|
71
|
-
- name: "2.6 including Code Climate"
|
72
|
-
commands:
|
73
|
-
- checkout
|
74
|
-
- sem-version ruby 2.6
|
75
|
-
- cache restore
|
76
|
-
- bundle install
|
77
|
-
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
|
78
|
-
- chmod +x ./cc-test-reporter
|
79
|
-
- cache store
|
80
|
-
- ./cc-test-reporter before-build
|
81
|
-
- bundle exec rspec
|
82
|
-
- ./cc-test-reporter after-build
|
83
|
-
|
84
|
-
- name: "jruby-9.1.17.0"
|
85
|
-
commands:
|
86
|
-
- checkout
|
87
|
-
- sem-version ruby jruby-9.1.17.0
|
88
|
-
- cache restore
|
89
|
-
- gem install bundler
|
90
|
-
- bundle install
|
91
|
-
- cache store
|
92
|
-
- bundle exec rspec
|
93
|
-
- name: "jruby-9.2.0.0"
|
94
|
-
commands:
|
95
|
-
- checkout
|
96
|
-
- sem-version ruby jruby-9.2.0.0
|
97
|
-
- cache restore
|
98
|
-
- gem install bundler
|
99
|
-
- bundle install
|
100
|
-
- cache store
|
101
|
-
- bundle exec rspec
|