cmxl 1.4.6 → 2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fe9599937da1fa4217f44747b0214af2111514da12832eea323afca3ce1bf11b
4
- data.tar.gz: 0b9bd79c985742fcfcaab730fe69eea91dc2d50b9d0cc1f3f96e6c15b50a3d16
3
+ metadata.gz: 1c172d6b56767a8860f57c4b802c7ed2ed2bae3658f6db104f44795a377bf067
4
+ data.tar.gz: ececb262e0d6a40c97e7ca54dcac08208e1942792c5f493be179ba3c81ece6b7
5
5
  SHA512:
6
- metadata.gz: 48ce33cbd21b7df6dce69cc610ccd02efd01df7b2c080be21b47682ad8754b641ee0598795ff9ec380373cab2679cc0235f344e986e1c274d427a435895c2e5c
7
- data.tar.gz: 0ee463246d09088cb8d4f9c7aa3979d19db3a4f487f8efdd359e01050503714d20e88a6fa40e1fd226dc83f785a031269516ca466b4ad974bc8c048127c72e50
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
- [![Build Status](https://railslove.semaphoreci.com/badges/cmxl/branches/master.svg?style=shields)](https://railslove.semaphoreci.com/branches/96e7bf8e-be69-4f01-878c-be3dac0e6e8d)
2
- [![Code Climate](https://codeclimate.com/github/railslove/cmxl/badges/gpa.svg)](https://codeclimate.com/github/railslove/cmxl)
3
- [![Test Coverage](https://codeclimate.com/github/railslove/cmxl/badges/coverage.svg)](https://codeclimate.com/github/railslove/cmxl)
4
- [![Gem Version](https://badge.fury.io/rb/cmxl.svg)](http://badge.fury.io/rb/cmxl)
1
+ [![CI](https://github.com/railslove/cmxl/actions/workflows/ci.yml/badge.svg)](https://github.com/railslove/cmxl/actions/workflows/ci.yml)
2
+ [![Gem Version](https://badge.fury.io/rb/cmxl.svg)](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 extendible MT940 bank statement file parser that helps your extracting data from the bank statement files.
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
- ## Reqirements
19
+ ## Requirements
22
20
 
23
21
  Cmxl is a pure ruby parser and has no dependency on native extensions.
24
22
 
25
- - Ruby 1.9.3 or newer (jruby, etc.)
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.funds_code
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 favorit JSON library.
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 wirednesses
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 wirednesses as much as possible. If no encoding is passed we try to guess the encoding of the data and convert it to UTF8.
95
- In the likely case that you encouter encoding issues you can pass encoding options to the `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)
96
- If that fails try to motify the file before you pass it to the parser - and please create an issue.
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 execpt the actual MT940 fields.
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
- 2014 - built with love by [Railslove](http://railslove.com) and released under the MIT-Licence. We have built quite a number of FinTech products. If you need support we are happy to help. Please contact us at team@railslove.com.
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 statment line we look for a matching entry or use the Unknown class as default
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})?(?<storno_flag>R?)(?<funds_code>[CD]{1})(?<currency_letter>[a-zA-Z])?(?<amount>\d{1,12},\d{0,2})(?<swift_code>(?:N|F).{3})(?<reference>NONREF|(.(?!\/\/)){,16}([^\/]){,1})((?:\/\/)(?<bank_reference>[^\n]{,16}))?((?:\n)(?<supplementary>.{,34}))?$}
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
- data['funds_code'].to_s.casecmp('C').zero?
18
+ credit_debit_indicator.include?('C')
19
19
  end
20
20
 
21
21
  def debit?
22
- data['funds_code'].to_s.casecmp('D').zero?
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
- !storno_flag.empty?
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
- data.values_at('storno_flag', 'funds_code').join
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
- data['storno_flag']
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' => storno?,
144
- 'funds_code' => funds_code,
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,
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Cmxl
2
- VERSION = '1.4.6'.freeze
2
+ VERSION = '2.0'.freeze
3
3
  end
data/lib/cmxl.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  require 'cmxl/version'
2
2
 
3
- require 'rchardet19'
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, silent: true)
35
- options[:encoding] = cd.encoding
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(:valuta_after_enty_date) { Cmxl::Fields::Transaction.parse(fixture[3]) }
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 correclty when including year-change' do
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(valuta_after_enty_date.date).to eql(Date.new(2014, 12, 24))
91
- expect(valuta_after_enty_date.entry_date).to eql(Date.new(2015, 1, 2))
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
@@ -2,3 +2,5 @@
2
2
  :61:1409010902RCR000000000001,62NTRF0000549855700010//025498557/000001
3
3
  :61:1401101224CR000000000001,62NTRF0000549855700010//025498557/000001
4
4
  :61:1412240102CR000000000001,62NTRF0000549855700010//025498557/000001
5
+ :61:1911181118CR653,00S445328556-76501096
6
+
@@ -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
+
@@ -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
@@ -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(3)
75
+ expect(subject.size).to eq(4)
69
76
  end
70
77
  end
71
78
 
@@ -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: 1.4.6
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: 2019-12-17 00:00:00.000000000 Z
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.0.2
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:
@@ -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