cmxl 0.0.4 → 0.0.5

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
  SHA1:
3
- metadata.gz: 099dfa2701305989d3afe020ee6fee473d75a50e
4
- data.tar.gz: 74e6f6ee642e4d3680b17b977ac470ef6ed1b1a5
3
+ metadata.gz: 18a7814325172343b3ae6aee190378823aacd8ab
4
+ data.tar.gz: a87474a066345e76dc8b66a15b83a632c26a5c0b
5
5
  SHA512:
6
- metadata.gz: 3635643d3f1966515e350da68bff1c46b0960508b085953338ad2f275eccb87e80fad2b8e7723ddbc526c64b4e2e036a9771e9c0343300e9fe5f613c34333364
7
- data.tar.gz: 74761935463086b4464f94d64bf77a06d6ec5b5914f5def5ce1840ec491a01ee67a544643096454a15998977184ad174090fd959a1c26007dd857cfd27acb478
6
+ metadata.gz: 4756b37a6bf9dc970b76d258dd95fef711f86697e64ae9a28c708efc6696ee6df5b076e2d24546bcd977e9fb1481bc1f5a4058eecfe7348b7fb7d9dba2f1446f
7
+ data.tar.gz: 1baadae4a20e53efc0f5f9a5bdd04c4e7cd7773f7ec04c468a768d36bddb1a86f96800ddcb28c863aeeaacb06829547ee64fd700809adc0a96f2eea0758f604d
data/README.md CHANGED
@@ -131,6 +131,10 @@ other parsers:
131
131
  * [betterplace/mt940_parser](https://github.com/betterplace/mt940_parser)
132
132
  * [gmitrev/mt940parser](https://github.com/gmitrev/mt940parser)
133
133
 
134
+ ## Stats
135
+
136
+ [![Build Status](https://magnum.travis-ci.com/railslove/cmxl.svg?token=e6QUckhTMdWWujkwZNBD&branch=master)](https://magnum.travis-ci.com/railslove/cmxl)
137
+
134
138
  ------------
135
139
 
136
140
  2014 - built with love by [Railslove](http://railslove.com). 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.
@@ -13,6 +13,8 @@ Gem::Specification.new do |spec|
13
13
  spec.homepage = "https://github.com/railslove/cmxl"
14
14
  spec.license = "MIT"
15
15
 
16
+ spec.post_install_message = "Thanks for using Cmxl - your friendly MT940 parser!\nWe hope we can make dealing with MT940 files a bit more fun. :) \nPlease create an issue on github if anything is not as expected.\n\n"
17
+
16
18
  spec.files = `git ls-files -z`.split("\x0")
17
19
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
20
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
@@ -5,8 +5,20 @@ require 'cmxl/statement'
5
5
  require 'cmxl/transaction'
6
6
  Dir[File.join(File.dirname(__FILE__), 'cmxl/fields', '*.rb')].each { |f| require f; }
7
7
  module Cmxl
8
-
9
8
  STATEMENT_SEPARATOR = "\n-"
9
+
10
+ # Public: Parse a MT940 string
11
+ #
12
+ # data - The String containing the MT940
13
+ # options - Hash with encoding options. Accepts the same parameters as String#encode!
14
+ # It is likely that you want to provide the encoding of your MT940 String
15
+ #
16
+ # Examples
17
+ #
18
+ # Cmxl.parse(File.read('mt940.txt'), encoding: 'ISO-8859-1')
19
+ # Cmxl.parse(mt940_string)
20
+ #
21
+ # Returns an array of Statement objects
10
22
  def self.parse(data, options={})
11
23
  options[:universal_newline] ||= true
12
24
  if options[:encoding]
@@ -11,13 +11,21 @@ module Cmxl
11
11
  DATE = /(?<year>\d{0,2})(?<month>\d{2})(?<day>\d{2})/
12
12
  attr_accessor :source, :modifier, :match, :data, :tag
13
13
 
14
+ # The parser class variable is the registry of all available parser.
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
14
17
  @@parsers = {}
15
18
  @@parsers.default = Unknown
16
19
  def self.parsers; @@parsers; end
17
20
 
21
+ # Class accessor for the parser
22
+ # Every sub class should have its own parser (regex to parse a MT940 field/line)
23
+ # The default parser matches the whole line
18
24
  class << self; attr_accessor :parser; end
19
- self.parser = /(?<source>.*)/ # default parser
25
+ self.parser = /(?<details>.*)/ # default parser
20
26
 
27
+ # Class accessor for the tag every defines a MT940 tag it can parse
28
+ # This also adds the class to the parser registry.
21
29
  def self.tag=(tag)
22
30
  @tag = tag.to_s
23
31
  @@parsers[tag.to_s] = self
@@ -26,6 +34,15 @@ module Cmxl
26
34
  @tag
27
35
  end
28
36
 
37
+ # Public: Parses a statement line and initiates a matching Field class
38
+ #
39
+ # Returns an instance of the special field class for the matched line.
40
+ # Raises and LineFormatError if the line is not well formatted
41
+ #
42
+ # Example:
43
+ #
44
+ # Cmxl::Field.parse(':60F:C031002PLN40000,00') #=> returns an AccountBalance instance
45
+ #
29
46
  def self.parse(line)
30
47
  if line.match(/^:(\d{2,2})(\w)?:(.*)$/)
31
48
  tag, modifier, content = $1, $2, $3
@@ -49,16 +66,26 @@ module Cmxl
49
66
  end
50
67
 
51
68
  def to_h
52
- self.data
69
+ self.data.merge('tag' => self.tag)
53
70
  end
54
71
  alias :to_hash :to_h
55
72
  def to_json(*args)
56
73
  to_h.to_json(*args)
57
74
  end
58
75
 
76
+ # Internal: Converts a provided string into a date object
77
+ # In MT940 documents the date is provided as a 6 char string (YYMMDD) or as a 4 char string (MMDD)
78
+ # If a 4 char string is provided a second parameter with the year should be provided. If no year is present the current year is assumed.
79
+ #
80
+ # Example:
81
+ #
82
+ # to_date('140909')
83
+ # to_date('0909', 2014)
84
+ #
85
+ # Retuns a date object or the provided date value if it is not parseable.
59
86
  def to_date(date, year=nil)
60
87
  if match = date.to_s.match(DATE)
61
- year ||= "20#{match['year']}"
88
+ year ||= "20#{match['year'] || Date.today.strftime("%y")}"
62
89
  month = match['month']
63
90
  day = match['day']
64
91
  Date.new(year.to_i, month.to_i, day.to_i)
@@ -29,7 +29,7 @@ module Cmxl
29
29
  end
30
30
 
31
31
  def to_h
32
- {
32
+ super.merge({
33
33
  'date' => date,
34
34
  'funds_code' => funds_code,
35
35
  'credit' => credit?,
@@ -38,7 +38,7 @@ module Cmxl
38
38
  'amount' => amount,
39
39
  'amount_in_cents' => amount_in_cents,
40
40
  'sign' => sign
41
- }
41
+ })
42
42
  end
43
43
  end
44
44
  end
@@ -17,11 +17,11 @@ module Cmxl
17
17
  end
18
18
 
19
19
  def amount
20
- self.data['amount'].gsub(',','.').to_f * sign
20
+ to_amount(self.data['amount'])
21
21
  end
22
22
 
23
23
  def amount_in_cents
24
- self.data['amount'].gsub(',', '').gsub('.','').to_i * sign
24
+ to_amount_in_cents(self.data['amount'])
25
25
  end
26
26
 
27
27
  def date
@@ -32,15 +32,20 @@ module Cmxl
32
32
  end
33
33
 
34
34
  def to_h
35
- super.merge({
35
+ {
36
36
  'date' => date,
37
37
  'entry_date' => entry_date,
38
38
  'amount' => amount,
39
39
  'amount_in_cents' => amount_in_cents,
40
40
  'sign' => sign,
41
41
  'debit' => debit?,
42
- 'credit' => credit?
43
- })
42
+ 'credit' => credit?,
43
+ 'funds_code' => funds_code,
44
+ 'swift_code' => swift_code,
45
+ 'reference' => reference,
46
+ 'bank_reference' => bank_reference,
47
+ 'currency_letter' => currency_letter
48
+ }
44
49
  end
45
50
  end
46
51
  end
@@ -3,6 +3,12 @@ module Cmxl
3
3
  class Statement
4
4
  attr_accessor :source, :collection, :transactions, :fields, :lines
5
5
 
6
+ # Public: Initiate a new Statement and parse a provided single statement string
7
+ # It directly parses the source and initiates file and transaction objects.
8
+ #
9
+ # Example:
10
+ #
11
+ # Cmxl::Statement.new(single_statement_string)
6
12
  def initialize(source)
7
13
  self.source = source
8
14
  self.fields = []
@@ -11,8 +17,10 @@ module Cmxl
11
17
  self.parse!
12
18
  end
13
19
 
20
+ # Internal: Parse a single MT940 statement and extract the line data
21
+ #
14
22
  def parse!
15
- # first we clean uo the source and make concat wraped lines
23
+ # first we clean uo the source and make concat wraped lines (lines not starting with a ":")
16
24
  self.source.split("\n").each do |line|
17
25
  if line.start_with?(':') || self.lines.last.nil?
18
26
  self.lines << line.strip
@@ -20,7 +28,7 @@ module Cmxl
20
28
  self.lines.last << line.strip
21
29
  end
22
30
  end
23
- # now we check each line. if it is part of a transaction we initate or update a transaction else we parse the field and add it to the fields collection
31
+ # Now we check each line for its content ans structure it for further use. If it is part of a transaction we initate or update a transaction else we parse the field and add it to the fields collection
24
32
  self.lines.each do |line|
25
33
  if line.match(/\A:61:/)
26
34
  self.transactions << Cmxl::Transaction.new(line)
@@ -32,6 +40,10 @@ module Cmxl
32
40
  end
33
41
  end
34
42
 
43
+ # Public: SHA2 of the provided source
44
+ # This is an experiment of trying to identify statements. The MT940 itself might not provide a unique identifier
45
+ #
46
+ # Retruns the SHA2 of the source
35
47
  def sha
36
48
  Digest::SHA2.new.update(self.source).to_s
37
49
  end
@@ -69,7 +81,8 @@ module Cmxl
69
81
  'opening_balance' => opening_balance,
70
82
  'closing_balance' => closing_balance,
71
83
  'available_balance' => available_balance,
72
- 'transactions' => transactions
84
+ 'transactions' => transactions,
85
+ 'fields' => fields
73
86
  }
74
87
  end
75
88
  alias :to_hash :to_h
@@ -77,6 +90,12 @@ module Cmxl
77
90
  to_h.to_json(*args)
78
91
  end
79
92
 
93
+ # Internal: Field accessor
94
+ # returns a field object by a given tag
95
+ #
96
+ # Example:
97
+ # field(20)
98
+ # field(61,'F')
80
99
  def field(tag, modifier=nil)
81
100
  self.fields.detect {|field| field.tag == tag.to_s && (modifier.nil? || field.modifier == modifier) }
82
101
  end
@@ -1,3 +1,3 @@
1
1
  module Cmxl
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.5"
3
3
  end
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  describe Cmxl::Field do
4
4
  subject { Cmxl::Field.new('D140829EUR000000000147,64') }
5
5
 
6
- it { expect(Cmxl::Field.parser).to eql(/(?<source>.*)/) } # default must be set
6
+ it { expect(Cmxl::Field.parser).to eql(/(?<details>.*)/) } # default must be set
7
7
 
8
8
  it { expect(subject.to_amount('123.')).to eql(123.00) }
9
9
  it { expect(subject.to_amount('123.1')).to eql(123.10) }
@@ -21,7 +21,8 @@ describe Cmxl::Fields::AccountBalance do
21
21
  'currency' =>"EUR",
22
22
  'amount' =>147.64,
23
23
  'amount_in_cents' =>14764,
24
- 'sign' =>1
24
+ 'sign' =>1,
25
+ 'tag' => '60'
25
26
  }) }
26
27
  end
27
28
 
@@ -8,5 +8,5 @@ describe Cmxl::Fields::Reference do
8
8
  it { expect(subject.date).to eql(Date.new(2014,9,2)) }
9
9
  it { expect(subject.statement_identifier).to eql('D') }
10
10
  it { expect(subject.additional_number).to eql('049') }
11
- it { expect(subject.to_h).to eql({"statement_identifier"=>"D", "date"=> Date.new(2014,9,2), "additional_number"=>"049", "reference"=>"D140902049"}) }
11
+ it { expect(subject.to_h).to eql({"tag" => '20', "statement_identifier"=>"D", "date"=> Date.new(2014,9,2), "additional_number"=>"049", "reference"=>"D140902049"}) }
12
12
  end
@@ -8,8 +8,8 @@ describe Cmxl::Fields::StatementLine do
8
8
  it { expect(subject.entry_date).to eql(Date.new(2014,9,2)) }
9
9
  it { expect(subject.funds_code).to eql('D') }
10
10
  it { expect(subject.currency_letter).to eql('R') }
11
- it { expect(subject.amount).to eql(-1.62) }
12
- it { expect(subject.amount_in_cents).to eql(-162) }
11
+ it { expect(subject.amount).to eql(1.62) }
12
+ it { expect(subject.amount_in_cents).to eql(162) }
13
13
  it { expect(subject.swift_code).to eql('NTRF') }
14
14
  it { expect(subject.reference).to eql('0000549855700010') }
15
15
  it { expect(subject.bank_reference).to eql('025498557/000001') }
@@ -9,10 +9,10 @@ describe Cmxl::Transaction do
9
9
  it { expect(subject.funds_code).to eql('D') }
10
10
  it { expect(subject.date).to eql(Date.new(2014,9,1))}
11
11
  it { expect(subject.entry_date).to eql(Date.new(2014,9,2))}
12
- it { expect(subject.amount).to eql(-1.62)}
13
- it { expect(subject.statement_line.amount).to eql(-1.62)}
14
- it { expect(subject.amount_in_cents).to eql(-162)}
15
- it { expect(subject.to_h).to eql({"date"=>Date.new(2014,9,1), "entry_date"=>Date.new(2014,9,2), "funds_code"=>"D", "currency_letter"=>"R", "amount"=>-1.62, "swift_code"=>"NTRF", "reference"=>"0000549855700010", "bank_reference"=>"025498557/000001", "amount_in_cents"=>-162, "sign"=>-1, "debit"=>true, "credit"=>false, "bic"=>"HYVEDEMMXXX", "iban"=>"HUkkbbbsssskcccccccccccccccx", "name"=>"Peter Pan", "sepa"=>{"EREF"=>"TRX-0A4A47C3-F846-4729-8A1B-5DF620F", "MREF"=>"CAC97D2144174318AC18D9BF815BD4FB", "CRED"=>"DE98ZZZ09999999999", "SVWZ"=>"FOO TRX-0A4A47C3-F846-4729-8A1B-5DF620F"}, "information"=>"EREF+TRX-0A4A47C3-F846-4729-8A1B-5DF620FMREF+CAC97D2144174318AC18D9BF815BD4FBCRED+DE98ZZZ09999999999SVWZ+FOO TRX-0A4A47C3-F846-4729-8A1B-5DF620F", "description"=>"SEPA LASTSCHRIFT KUNDE", "sub_fields"=>{"00"=>"SEPA LASTSCHRIFT KUNDE", "10"=>"281", "20"=>"EREF+TRX-0A4A47C3-F846-4729", "21"=>"-8A1B-5DF620F", "22"=>"MREF+CAC97D2144174318AC18D9", "23"=>"BF815BD4FB", "24"=>"CRED+DE98ZZZ09999999999", "25"=>"SVWZ+FOO TRX-0A4A47C3-F84", "26"=>"6-4729-8A1B-5DF620F", "30"=>"HYVEDEMMXXX", "31"=>"HUkkbbbsssskcccccccccccccccx", "32"=>"Peter Pan", "34"=>"171"}, "transaction_code"=>"171", "details"=>"?00SEPA LASTSCHRIFT KUNDE?10281?20EREF+TRX-0A4A47C3-F846-4729?21-8A1B-5DF620F?22MREF+CAC97D2144174318AC18D9?23BF815BD4FB?24CRED+DE98ZZZ09999999999?25SVWZ+FOO TRX-0A4A47C3-F84?266-4729-8A1B-5DF620F?30HYVEDEMMXXX?31HUkkbbbsssskcccccccccccccccx?32Peter Pan?34171"}) }
12
+ it { expect(subject.amount).to eql(1.62)}
13
+ it { expect(subject.statement_line.amount).to eql(1.62)}
14
+ it { expect(subject.amount_in_cents).to eql(162)}
15
+ it { expect(subject.to_h).to eql({"date"=>Date.new(2014,9,1), "entry_date"=>Date.new(2014,9,2), "funds_code"=>"D", "currency_letter"=>"R", "amount"=>1.62, "swift_code"=>"NTRF", "reference"=>"0000549855700010", "bank_reference"=>"025498557/000001", "amount_in_cents"=>162, "sign"=>-1, "debit"=>true, "credit"=>false, "bic"=>"HYVEDEMMXXX", "iban"=>"HUkkbbbsssskcccccccccccccccx", "name"=>"Peter Pan", "sepa"=>{"EREF"=>"TRX-0A4A47C3-F846-4729-8A1B-5DF620F", "MREF"=>"CAC97D2144174318AC18D9BF815BD4FB", "CRED"=>"DE98ZZZ09999999999", "SVWZ"=>"FOO TRX-0A4A47C3-F846-4729-8A1B-5DF620F"}, "information"=>"EREF+TRX-0A4A47C3-F846-4729-8A1B-5DF620FMREF+CAC97D2144174318AC18D9BF815BD4FBCRED+DE98ZZZ09999999999SVWZ+FOO TRX-0A4A47C3-F846-4729-8A1B-5DF620F", "description"=>"SEPA LASTSCHRIFT KUNDE", "sub_fields"=>{"00"=>"SEPA LASTSCHRIFT KUNDE", "10"=>"281", "20"=>"EREF+TRX-0A4A47C3-F846-4729", "21"=>"-8A1B-5DF620F", "22"=>"MREF+CAC97D2144174318AC18D9", "23"=>"BF815BD4FB", "24"=>"CRED+DE98ZZZ09999999999", "25"=>"SVWZ+FOO TRX-0A4A47C3-F84", "26"=>"6-4729-8A1B-5DF620F", "30"=>"HYVEDEMMXXX", "31"=>"HUkkbbbsssskcccccccccccccccx", "32"=>"Peter Pan", "34"=>"171"}, "transaction_code"=>"171", "details"=>"?00SEPA LASTSCHRIFT KUNDE?10281?20EREF+TRX-0A4A47C3-F846-4729?21-8A1B-5DF620F?22MREF+CAC97D2144174318AC18D9?23BF815BD4FB?24CRED+DE98ZZZ09999999999?25SVWZ+FOO TRX-0A4A47C3-F84?266-4729-8A1B-5DF620F?30HYVEDEMMXXX?31HUkkbbbsssskcccccccccccccccx?32Peter Pan?34171"}) }
16
16
  end
17
17
 
18
18
  context 'without details' do
@@ -21,8 +21,8 @@ describe Cmxl::Transaction do
21
21
  it { expect(subject).to_not be_credit }
22
22
  it { expect(subject.date).to eql(Date.new(2014,9,1))}
23
23
  it { expect(subject.entry_date).to eql(Date.new(2014,9,2))}
24
- it { expect(subject.amount).to eql(-1.62)}
25
- it { expect(subject.amount_in_cents).to eql(-162)}
26
- it { expect(subject.to_h).to eql({"date"=>Date.new(2014,9,1), "entry_date"=>Date.new(2014,9,2), "funds_code"=>"D", "currency_letter"=>"R", "amount"=>-1.62, "swift_code"=>"NTRF", "reference"=>"0000549855700010", "bank_reference"=>"025498557/000001", "amount_in_cents"=>-162, "sign"=>-1, "debit"=>true, "credit"=>false}) }
24
+ it { expect(subject.amount).to eql(1.62)}
25
+ it { expect(subject.amount_in_cents).to eql(162)}
26
+ it { expect(subject.to_h).to eql({"date"=>Date.new(2014,9,1), "entry_date"=>Date.new(2014,9,2), "funds_code"=>"D", "currency_letter"=>"R", "amount"=>1.62, "swift_code"=>"NTRF", "reference"=>"0000549855700010", "bank_reference"=>"025498557/000001", "amount_in_cents"=>162, "sign"=>-1, "debit"=>true, "credit"=>false}) }
27
27
  end
28
28
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cmxl
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Bumann
@@ -109,7 +109,9 @@ homepage: https://github.com/railslove/cmxl
109
109
  licenses:
110
110
  - MIT
111
111
  metadata: {}
112
- post_install_message:
112
+ post_install_message: "Thanks for using Cmxl - your friendly MT940 parser!\nWe hope
113
+ we can make dealing with MT940 files a bit more fun. :) \nPlease create an issue
114
+ on github if anything is not as expected.\n\n"
113
115
  rdoc_options: []
114
116
  require_paths:
115
117
  - lib