norma43_parser 4.0.0 → 4.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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +6 -0
  3. data/.github/workflows/ci.yml +1 -1
  4. data/.github/workflows/release.yml +2 -2
  5. data/.rspec +1 -0
  6. data/CHANGELOG.md +17 -2
  7. data/Gemfile.lock +2 -23
  8. data/README.md +1 -1
  9. data/lib/norma43/line_parsers/transaction.rb +1 -1
  10. data/lib/norma43/models/account.rb +51 -19
  11. data/lib/norma43/models/additional_currency.rb +13 -7
  12. data/lib/norma43/models/additional_item.rb +11 -6
  13. data/lib/norma43/models/document.rb +30 -11
  14. data/lib/norma43/models/mixins/attributes_assignment.rb +33 -0
  15. data/lib/norma43/models/transaction.rb +41 -15
  16. data/lib/norma43/models.rb +0 -2
  17. data/lib/norma43/version.rb +1 -1
  18. data/norma43_parser.gemspec +0 -1
  19. data/spec/example1_parse_spec.rb +1 -1
  20. data/spec/example2_parse_spec.rb +86 -0
  21. data/spec/fixtures/example2.n43 +13 -0
  22. data/spec/norma43/line_parsers/transaction_spec.rb +1 -1
  23. data/spec/norma43/line_processors/account_end_spec.rb +1 -4
  24. data/spec/norma43/line_processors/account_start_spec.rb +3 -3
  25. data/spec/norma43/line_processors/additional_currency_spec.rb +3 -3
  26. data/spec/norma43/line_processors/{additional_items_spec.rb → additional_item_spec.rb} +3 -3
  27. data/spec/norma43/line_processors/document_end_spec.rb +3 -6
  28. data/spec/norma43/line_processors/document_start_spec.rb +3 -3
  29. data/spec/norma43/line_processors/transaction_spec.rb +3 -3
  30. data/spec/norma43/models/account_spec.rb +39 -5
  31. data/spec/norma43/models/document_spec.rb +35 -0
  32. data/spec/norma43/models/transaction_spec.rb +25 -0
  33. data/spec/norma43_spec.rb +1 -1
  34. data/spec/spec_helper.rb +3 -0
  35. data/spec/support/shared_examples_for_models.rb +61 -0
  36. metadata +21 -22
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '0449ec18860a4308816374b89703797ec359a8032b15a3f3fb3e4a8749dbdc44'
4
- data.tar.gz: 0d0438fb23ba70c0b5c3ce107e7ac6e71dd5007ceb2fe64887aaf0d61c46cf60
3
+ metadata.gz: 6a576ebd4936bf3b9f7c190dd6f9024f6581efabca4141d8383ebdd16c4cdb9d
4
+ data.tar.gz: '079b2f02c83c570abe052d635d82d7ad78919b18341de0d81fe2f22e62a64108'
5
5
  SHA512:
6
- metadata.gz: dd5abd1c1b2893eb6fcb8c2025d659c1c5876198d68d624e19e18ac1975aecb48c557f7a27070c690f0090028d630a6d2ece30daf7daad70eda3993a736e56fe
7
- data.tar.gz: 5431b59a68a1f3ce474e31aa44735ad9a6c1863deb20e157375e8b81938192d99e6184f587b6eadefffd992bf6fd5398acea5e261d2100f70be4bd59d4b019f3
6
+ metadata.gz: 5b54505e514e0f3e226b6d910963570a753686db02f8bc6229fcb2f6586b79153fb914f8a2ed0c0ad73a4381a72c7f1ff5e9f2333c6d21ee3dd6e9bdecc03151
7
+ data.tar.gz: cffc3991bef02c3fe166d711db5e9c7d5d3d804eeb9b02d1188679fcfd84d46b511de0ce064c2eded074bdffd9d925817385f545553383459e8c428dd0f2006b
@@ -0,0 +1,6 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "github-actions"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"
@@ -12,7 +12,7 @@ jobs:
12
12
  ruby-version: ["3.3", "3.2", "3.1", "3.0", "jruby-head"]
13
13
 
14
14
  steps:
15
- - uses: actions/checkout@v2
15
+ - uses: actions/checkout@v4
16
16
  - name: Set up Ruby ${{ matrix.ruby-version }}
17
17
  uses: ruby/setup-ruby@v1
18
18
  with:
@@ -7,14 +7,14 @@ jobs:
7
7
  release:
8
8
  runs-on: ubuntu-latest
9
9
  steps:
10
- - uses: GoogleCloudPlatform/release-please-action@v3
10
+ - uses: GoogleCloudPlatform/release-please-action@v4
11
11
  id: release
12
12
  with:
13
13
  release-type: ruby
14
14
  package-name: norma43_parser
15
15
  bump-minor-pre-major: true
16
16
  version-file: 'lib/norma43/version.rb'
17
- - uses: actions/checkout@v2
17
+ - uses: actions/checkout@v4
18
18
  if: ${{ steps.release.outputs.release_created }}
19
19
  - uses: ruby/setup-ruby@v1
20
20
  with:
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --require spec_helper
data/CHANGELOG.md CHANGED
@@ -1,6 +1,21 @@
1
- ## [4.0.0](https://github.com/sequra/norma43_parser/compare/3.0.0...v4.0.0) (2024-06-18)
1
+ ## [4.1.0](https://github.com/sequra/norma43_parser/compare/v4.0.0...v4.1.0) (2024-06-20)
2
+
3
+ ### Features
4
+
5
+ * Remove virtus dependency ([#35](https://github.com/sequra/norma43_parser/issues/35))
6
+
7
+ * Soft-deprecate `Norma43::Models::Document#transaction_date`, use transaction_date in transactions instead
8
+
9
+
10
+ ## [4.2.0](https://github.com/sequra/norma43_parser/compare/v4.1.0...v4.2.0) (2025-03-25)
2
11
 
3
12
 
13
+ ### Features
14
+
15
+ * [PAY-1007] Parse `reference_1` as string to be able to filter lines ([#42](https://github.com/sequra/norma43_parser/issues/42)) ([f60f4e4](https://github.com/sequra/norma43_parser/commit/f60f4e4f396e0e4ccfba22e59018a8e97a39dd47))
16
+
17
+ ## [4.0.0](https://github.com/sequra/norma43_parser/compare/3.0.0...v4.0.0) (2024-06-18)
18
+
4
19
  ### ⚠ BREAKING CHANGES
5
20
 
6
21
  * Drop Ruby support below 3
@@ -22,13 +37,13 @@
22
37
 
23
38
  - Update rake requirement from ~> 10.0 to ~> 13.0 (#6)
24
39
 
40
+
25
41
  ## v2.0.0 (2019-10-17)
26
42
 
27
43
  ### Breaking Changes
28
44
 
29
45
  - Drop Ruby support below 2.4.
30
46
 
31
-
32
47
  ### Enhancements
33
48
 
34
49
  - Clean up dependencies
data/Gemfile.lock CHANGED
@@ -1,25 +1,14 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- norma43_parser (4.0.0)
5
- virtus (~> 1.0)
4
+ norma43_parser (4.2.0)
6
5
  zeitwerk (~> 2.0)
7
6
 
8
7
  GEM
9
8
  remote: https://rubygems.org/
10
9
  specs:
11
10
  ast (2.4.2)
12
- axiom-types (0.1.1)
13
- descendants_tracker (~> 0.0.4)
14
- ice_nine (~> 0.11.0)
15
- thread_safe (~> 0.3, >= 0.3.1)
16
- coercible (1.0.0)
17
- descendants_tracker (~> 0.0.1)
18
- descendants_tracker (0.0.4)
19
- thread_safe (~> 0.3, >= 0.3.1)
20
11
  diff-lcs (1.5.1)
21
- equalizer (0.0.11)
22
- ice_nine (0.11.2)
23
12
  json (2.7.2)
24
13
  json (2.7.2-java)
25
14
  language_server-protocol (3.17.0.3)
@@ -32,8 +21,7 @@ GEM
32
21
  rainbow (3.1.1)
33
22
  rake (13.2.1)
34
23
  regexp_parser (2.9.2)
35
- rexml (3.3.0)
36
- strscan
24
+ rexml (3.3.9)
37
25
  rspec (3.13.0)
38
26
  rspec-core (~> 3.13.0)
39
27
  rspec-expectations (~> 3.13.0)
@@ -64,16 +52,7 @@ GEM
64
52
  rubocop (>= 1.48.1, < 2.0)
65
53
  rubocop-ast (>= 1.31.1, < 2.0)
66
54
  ruby-progressbar (1.13.0)
67
- strscan (3.1.0)
68
- strscan (3.1.0-java)
69
- thread_safe (0.3.6)
70
- thread_safe (0.3.6-java)
71
55
  unicode-display_width (2.5.0)
72
- virtus (1.0.5)
73
- axiom-types (~> 0.1)
74
- coercible (~> 1.0)
75
- descendants_tracker (~> 0.0, >= 0.0.3)
76
- equalizer (~> 0.0, >= 0.0.9)
77
56
  zeitwerk (2.6.15)
78
57
 
79
58
  PLATFORMS
data/README.md CHANGED
@@ -62,7 +62,7 @@ amount_code=2,
62
62
  document_number=0,
63
63
  origin_branch_code=123,
64
64
  own_item=16,
65
- reference_1=0,
65
+ reference_1=nil,
66
66
  reference_2=nil,
67
67
  shared_item=4,
68
68
  transaction_date=Tue, 04 Feb 2016,
@@ -11,7 +11,7 @@ module Norma43
11
11
  field :amount_code, 27, :integer
12
12
  field :amount, 28..41, :integer
13
13
  field :document_number, 42..51, :integer
14
- field :reference_1, 52..63, :integer
14
+ field :reference_1, 52..63
15
15
  field :reference_2, 64..79
16
16
  end
17
17
  end
@@ -1,30 +1,62 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "virtus"
4
-
5
3
  module Norma43
6
4
  module Models
7
5
  class Account
8
- include Virtus.model
6
+ include Mixins::AttributesAssignment
7
+
8
+ attr_accessor :bank_code,
9
+ :branch_code,
10
+ :account_number,
11
+ :start_date,
12
+ :end_date,
13
+ :balance_code,
14
+ :balance_amount,
15
+ :currency_code,
16
+ :information_mode_code,
17
+ :abbreviated_name,
18
+ :debit_entries,
19
+ :debit_amount,
20
+ :credit_entries,
21
+ :credit_amount,
22
+ :transactions
9
23
 
10
- attribute :bank_code
11
- attribute :branch_code
12
- attribute :account_number
13
- attribute :start_date
14
- attribute :end_date
15
- attribute :balance_code
16
- attribute :balance_amount
17
- attribute :currency_code
18
- attribute :information_mode_code
19
- attribute :abbreviated_name
20
- attribute :debit_entries
21
- attribute :debit_amount
22
- attribute :credit_entries
23
- attribute :credit_amount
24
- attribute :transactions, Array[Transaction]
24
+ def initialize(attributes = EMPTY_ATTRIBUTES)
25
+ @bank_code,
26
+ @branch_code,
27
+ @account_number,
28
+ @start_date,
29
+ @end_date,
30
+ @balance_code,
31
+ @balance_amount,
32
+ @currency_code,
33
+ @information_mode_code,
34
+ @abbreviated_name,
35
+ @debit_entries,
36
+ @debit_amount,
37
+ @credit_entries,
38
+ @credit_amount,
39
+ transactions = Hash(attributes).values_at(
40
+ :bank_code,
41
+ :branch_code,
42
+ :account_number,
43
+ :start_date,
44
+ :end_date,
45
+ :balance_code,
46
+ :balance_amount,
47
+ :currency_code,
48
+ :information_mode_code,
49
+ :abbreviated_name,
50
+ :debit_entries,
51
+ :debit_amount,
52
+ :credit_entries,
53
+ :credit_amount,
54
+ :transactions)
55
+ @transactions = Array(transactions).map { |attrs| Transaction.new(attrs) }
56
+ end
25
57
 
26
58
  def iban
27
- @iban ||= SpanishIban.from_account(self)
59
+ SpanishIban.from_account(self)
28
60
  end
29
61
  end
30
62
  end
@@ -1,16 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "virtus"
4
-
5
3
  module Norma43
6
4
  module Models
7
5
  class AdditionalCurrency
8
- include Virtus.model
6
+ include Mixins::AttributesAssignment
7
+
8
+ attr_accessor :data_code, :currency_code, :amount, :free
9
9
 
10
- attribute :data_code
11
- attribute :currency_code
12
- attribute :amount
13
- attribute :free
10
+ def initialize(attributes = EMPTY_ATTRIBUTES)
11
+ @data_code,
12
+ @currency_code,
13
+ @amount,
14
+ @free = Hash(attributes).values_at(
15
+ :data_code,
16
+ :currency_code,
17
+ :amount,
18
+ :free)
19
+ end
14
20
  end
15
21
  end
16
22
  end
@@ -1,15 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "virtus"
4
-
5
3
  module Norma43
6
4
  module Models
7
5
  class AdditionalItem
8
- include Virtus.model
6
+ include Mixins::AttributesAssignment
7
+
8
+ attr_accessor :data_code, :item_1, :item_2
9
9
 
10
- attribute :data_code
11
- attribute :item_1
12
- attribute :item_2
10
+ def initialize(attributes = EMPTY_ATTRIBUTES)
11
+ @data_code,
12
+ @item_1,
13
+ @item_2 = Hash(attributes).values_at(
14
+ :data_code,
15
+ :item_1,
16
+ :item_2)
17
+ end
13
18
  end
14
19
  end
15
20
  end
@@ -1,22 +1,41 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "virtus"
4
-
5
3
  module Norma43
6
4
  module Models
7
5
  class Document
8
- include Virtus.model
6
+ include Mixins::AttributesAssignment
7
+
8
+ attr_accessor :id, :created_at, :delivery_number, :file_type, :name, :number_of_lines, :accounts
9
9
 
10
- attribute :id
11
- attribute :created_at
12
- attribute :delivery_number
13
- attribute :file_type
14
- attribute :name
15
- attribute :number_of_lines
16
- attribute :accounts, Array[Account]
10
+ def initialize(attributes = EMPTY_ATTRIBUTES)
11
+ @id,
12
+ @created_at,
13
+ @delivery_number,
14
+ @file_type,
15
+ @name,
16
+ @number_of_lines,
17
+ accounts = Hash(attributes).values_at(
18
+ :id,
19
+ :created_at,
20
+ :delivery_number,
21
+ :file_type,
22
+ :name,
23
+ :number_of_lines,
24
+ :accounts)
25
+ @accounts = Array(accounts).map { |account| Account.new(account) }
26
+ end
17
27
 
28
+ # @deprecated Please ask each transaction inside accounts for their transaction_date instead
18
29
  def transaction_date
19
- accounts.map(&:date).compact.first
30
+ warn "[DEPRECATION] `transaction_date` is deprecated, use `#transaction_date` from transactions in `#accounts` instead"
31
+ date = nil
32
+
33
+ accounts.flat_map(&:transactions).each { |transaction|
34
+ date = transaction&.transaction_date
35
+ break unless date.nil?
36
+ }
37
+
38
+ date
20
39
  end
21
40
  end
22
41
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Norma43
4
+ module Models
5
+ module Mixins
6
+ module AttributesAssignment
7
+ EMPTY_ATTRIBUTES = {}.freeze
8
+
9
+ def attributes=(new_attributes)
10
+ Hash(new_attributes).each do |attr_name, attr_value|
11
+ attr_writer_method_name = "#{attr_name}=".to_sym
12
+ next unless public_methods(false).include?(attr_writer_method_name)
13
+
14
+ public_send(attr_writer_method_name, attr_value)
15
+ end
16
+ end
17
+
18
+ def attributes
19
+ instance_variables.map { |ivar_name|
20
+ attr_reader_method_name = ivar_name.to_s.delete_prefix("@").to_sym
21
+ next unless public_methods(false).include?(attr_reader_method_name)
22
+
23
+ attr_value = public_send(attr_reader_method_name)
24
+
25
+ [attr_reader_method_name, attr_value]
26
+ }.compact.to_h
27
+ end
28
+ alias_method :to_hash, :attributes # Implicit coercion for `Hash(model)`
29
+ alias_method :to_h, :attributes # Explicit coercion
30
+ end
31
+ end
32
+ end
33
+ end
@@ -1,24 +1,50 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "virtus"
4
-
5
3
  module Norma43
6
4
  module Models
7
5
  class Transaction
8
- include Virtus.model
6
+ include Mixins::AttributesAssignment
7
+
8
+ attr_accessor :origin_branch_code,
9
+ :transaction_date,
10
+ :value_date,
11
+ :shared_item,
12
+ :own_item,
13
+ :amount_code,
14
+ :amount,
15
+ :document_number,
16
+ :reference_1,
17
+ :reference_2,
18
+ :additional_items,
19
+ :additional_currency
20
+
21
+ def initialize(attributes = EMPTY_ATTRIBUTES)
22
+ @origin_branch_code,
23
+ @transaction_date,
24
+ @value_date,
25
+ @shared_item,
26
+ @own_item,
27
+ @amount_code,
28
+ @amount,
29
+ @document_number,
30
+ @reference_1,
31
+ @reference_2,
32
+ additional_items,
33
+ additional_currency = Hash(attributes).values_at(
34
+ :origin_branch_code,
35
+ :transaction_date,
36
+ :value_date,
37
+ :shared_item,
38
+ :own_item,
39
+ :amount_code,
40
+ :amount,
41
+ :document_number,
42
+ :reference_1,
43
+ :reference_2)
44
+ @additional_items = Array(additional_items).map { |attrs| AdditionalItem.new(attrs) }
45
+ @additional_currency = AdditionalCurrency.new(additional_currency) if additional_currency
46
+ end
9
47
 
10
- attribute :origin_branch_code
11
- attribute :transaction_date
12
- attribute :value_date
13
- attribute :shared_item
14
- attribute :own_item
15
- attribute :amount_code
16
- attribute :amount
17
- attribute :document_number
18
- attribute :reference_1
19
- attribute :reference_2
20
- attribute :additional_items, Array[AdditionalItem]
21
- attribute :additional_currency, AdditionalCurrency
22
48
  def debit?; self.amount_code == DEBIT_CODE end
23
49
  end
24
50
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "virtus"
4
-
5
3
  module Norma43
6
4
  module Models
7
5
  DEBIT_CODE = 1
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Norma43
4
- VERSION = "4.0.0"
4
+ VERSION = "4.2.0"
5
5
  end
@@ -19,7 +19,6 @@ 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_runtime_dependency "virtus", "~> 1.0"
23
22
  spec.add_runtime_dependency "zeitwerk", "~> 2.0"
24
23
 
25
24
  spec.add_development_dependency "rake", "~> 13.0"
@@ -58,7 +58,7 @@ RSpec.describe Norma43 do
58
58
  "amount_code" => 2,
59
59
  "amount" => 1234,
60
60
  "document_number" => 0,
61
- "reference_1" => 0,
61
+ "reference_1" => nil,
62
62
  "reference_2" => nil,
63
63
  )
64
64
  end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "norma43"
4
+
5
+ RSpec.describe Norma43 do
6
+ describe "parse" do
7
+ let(:document) do
8
+ file = File.open(File.join(__dir__, "fixtures/example2.n43"),
9
+ encoding: "iso-8859-1")
10
+ Norma43.parse file
11
+ end
12
+
13
+ it "finds one account" do
14
+ expect(document.accounts.size).to eq 1
15
+ end
16
+
17
+ describe "first account" do
18
+ let(:account) { document.accounts.first }
19
+
20
+ it "stores expected attributes from AccountStart parser" do
21
+ expect(account).to have_attributes(
22
+ "bank_code" => 81,
23
+ "branch_code" => 4797,
24
+ "account_number" => 6995216857,
25
+ "start_date" => Date.parse("2025-03-17"),
26
+ "end_date" => Date.parse("2025-03-17"),
27
+ "currency_code" => 978,
28
+ "information_mode_code" => 3,
29
+ "abbreviated_name" => "SEQURA WORLDWIDE S.A.",
30
+ )
31
+ end
32
+
33
+ it "stores expected attributes from AccountEnd parser" do
34
+ expect(account).to have_attributes(
35
+ "balance_code" => 2,
36
+ "balance_amount" => 8614571,
37
+ "debit_entries" => nil,
38
+ "debit_amount" => nil,
39
+ "credit_entries" => nil,
40
+ "credit_amount" => nil,
41
+ )
42
+ end
43
+
44
+ describe "transactions" do
45
+ it "finds all transactions" do
46
+ expect(account.transactions.size).to eq 4
47
+ end
48
+
49
+ describe "first transaction" do
50
+ let(:transaction) { account.transactions[0] }
51
+ it "stores expected attributes" do
52
+ expect(transaction).to have_attributes(
53
+ "origin_branch_code" => 7013,
54
+ "transaction_date" => Date.parse("2025-03-17"),
55
+ "value_date" => Date.parse("2025-03-14"),
56
+ "shared_item" => 4,
57
+ "own_item" => 7,
58
+ "amount_code" => 2,
59
+ "amount" => 9726,
60
+ "document_number" => 6871166755,
61
+ "reference_1" => "TRANSFERENCI",
62
+ "reference_2" => "A48555633617",
63
+ )
64
+ end
65
+ end
66
+
67
+ it "each transaction has 5 additional_items as a maximum" do
68
+ account.transactions.each do |transaction|
69
+ expect(transaction.additional_items.size).to be <= 5
70
+ end
71
+ end
72
+
73
+ describe "first additional item" do
74
+ let(:additional_item) { account.transactions[0].additional_items[0] }
75
+ it "stores expected attributes" do
76
+ expect(additional_item).to have_attributes(
77
+ "data_code" => 1,
78
+ "item_1" => "MARIA NARANJO ENFLOR",
79
+ "item_2" => "A48555633617"
80
+ )
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,13 @@
1
+ 110081479769952168572503172503172000000086145719783SEQURA WORLDWIDE S.A.
2
+ 2201827013250317250314040072000000000097266871166755TRANSFERENCIA48555633617
3
+ 2301MARIA NARANJO ENFLOR A48555633617
4
+ 2302 01826874
5
+ 2201827013250317250317040072000000000074332011157110TRANSFERENCISEQURA
6
+ 2301LAURA MARTINEZ PEREZ
7
+ 2302 01822011
8
+ 2201828510250317250317990512000000000082250000000000BIZUM
9
+ 2301NAYARA;MARTINEZ RODRIGUEZ PEDIDO 322200000000
10
+ 2302 15830001
11
+ 2201828510250317250317990512000000000088940000000000BIZUM
12
+ 2301DANIEL GARCIA GARCIA PEDIDO 322254909000
13
+ 2302 21000900
@@ -42,7 +42,7 @@ module Norma43
42
42
  end
43
43
 
44
44
  it "parses the reference 1" do
45
- expect(transaction.reference_1).to eq 0
45
+ expect(transaction.reference_1).to be_nil
46
46
  end
47
47
 
48
48
  it "parses the reference 2" do
@@ -3,10 +3,7 @@
3
3
  module Norma43
4
4
  module LineProcessors
5
5
  RSpec.describe AccountEnd do
6
- let :line do
7
- double "Line", attributes: {}
8
- end
9
-
6
+ let(:line) { instance_double(LineParsers::AccountEnd, attributes: {}) }
10
7
  let(:account) { Norma43::Models::Account.new }
11
8
  let(:contexts) { Norma43::Utils::Contexts.new(
12
9
  [
@@ -2,8 +2,8 @@
2
2
 
3
3
  module Norma43
4
4
  module LineProcessors
5
- RSpec.describe "AccountStart" do
6
- let(:line) { double "Line", attributes: {} }
5
+ RSpec.describe AccountStart do
6
+ let(:line) { instance_double(LineParsers::AccountStart, attributes: {}) }
7
7
  let(:document) { Norma43::Models::Document.new }
8
8
  let(:contexts) { Norma43::Utils::Contexts.new }
9
9
 
@@ -20,7 +20,7 @@ module Norma43
20
20
  end
21
21
 
22
22
  context "when AccountStart is called" do
23
- let(:fake_account) { double "Norma43::Models::Account" }
23
+ let(:fake_account) { instance_double(Norma43::Models::Account) }
24
24
  before do
25
25
  allow(Norma43::Models::Account).to receive(:new) { fake_account }
26
26
  end
@@ -2,8 +2,8 @@
2
2
 
3
3
  module Norma43
4
4
  module LineProcessors
5
- RSpec.describe "AdditionalCurrency" do
6
- let(:line) { double "Line", attributes: {} }
5
+ RSpec.describe AdditionalCurrency do
6
+ let(:line) { instance_double(LineParsers::AdditionalCurrency, attributes: {}) }
7
7
  let(:transaction) { Norma43::Models::Transaction.new }
8
8
  let(:contexts) { Norma43::Utils::Contexts.new(
9
9
  [
@@ -22,7 +22,7 @@ module Norma43
22
22
  end
23
23
 
24
24
  context "when AdditionalCurrency is called" do
25
- let(:fake_additional_currency) { double "Models::AdditionalCurrency" }
25
+ let(:fake_additional_currency) { instance_double(Models::AdditionalCurrency) }
26
26
  before do
27
27
  allow(Models::AdditionalCurrency).to receive(:new) { fake_additional_currency }
28
28
  end
@@ -2,8 +2,8 @@
2
2
 
3
3
  module Norma43
4
4
  module LineProcessors
5
- RSpec.describe "AdditionalItems" do
6
- let(:line) { double "Line", attributes: {} }
5
+ RSpec.describe AdditionalItem do
6
+ let(:line) { instance_double(LineParsers::AdditionalItem, attributes: {}) }
7
7
  let(:transaction) { Norma43::Models::Transaction.new }
8
8
  let(:contexts) { Norma43::Utils::Contexts.new(
9
9
  [
@@ -22,7 +22,7 @@ module Norma43
22
22
  end
23
23
 
24
24
  context "when AdditionalItem is called" do
25
- let(:fake_additional_item) { double "Models::AdditionalItem" }
25
+ let(:fake_additional_item) { instance_double(Models::AdditionalItem) }
26
26
  before do
27
27
  allow(Models::AdditionalItem).to receive(:new) { fake_additional_item }
28
28
  end
@@ -2,15 +2,12 @@
2
2
 
3
3
  module Norma43
4
4
  module LineProcessors
5
- RSpec.describe "DocumentEnd" do
6
- class Thing
7
- end
8
-
9
- let(:line) { double "Line", record_number: 35 }
5
+ RSpec.describe DocumentEnd do
6
+ let(:line) { instance_double(LineParsers::DocumentEnd, record_number: 35) }
10
7
 
11
8
  it "moves to the nearest document context" do
12
9
  document = Norma43::Models::Document.new
13
- contexts = Norma43::Utils::Contexts.new [Thing.new, document, Thing.new, Thing.new]
10
+ contexts = Norma43::Utils::Contexts.new [anything, document, anything, anything]
14
11
 
15
12
  DocumentEnd.call line, contexts
16
13
 
@@ -2,8 +2,8 @@
2
2
 
3
3
  module Norma43
4
4
  module LineProcessors
5
- RSpec.describe "DocumentStart" do
6
- let (:line) { double "Line", attributes: {} }
5
+ RSpec.describe DocumentStart do
6
+ let(:line) { instance_double(LineParsers::DocumentStart, attributes: {}) }
7
7
 
8
8
  it "instantiates a new document with the line attributes" do
9
9
  allow(Models::Document).to receive :new
@@ -14,7 +14,7 @@ module Norma43
14
14
  end
15
15
 
16
16
  it "sets the document as the current context" do
17
- fake_document = double "Models::Document"
17
+ fake_document = instance_double(Models::Document)
18
18
  allow(Models::Document).to receive(:new) { fake_document }
19
19
 
20
20
  contexts = DocumentStart.call line, Norma43::Utils::Contexts.new
@@ -2,8 +2,8 @@
2
2
 
3
3
  module Norma43
4
4
  module LineProcessors
5
- RSpec.describe "Transaction" do
6
- let(:line) { double "Line", attributes: {} }
5
+ RSpec.describe Transaction do
6
+ let(:line) { instance_double(LineParsers::Transaction, attributes: {}) }
7
7
  let(:account) { Norma43::Models::Account.new }
8
8
  let(:contexts) { Norma43::Utils::Contexts.new(
9
9
  [
@@ -22,7 +22,7 @@ module Norma43
22
22
  end
23
23
 
24
24
  context "when Transaction is called" do
25
- let(:fake_transaction) { double "Models::Transaction" }
25
+ let(:fake_transaction) { instance_double(Models::Transaction) }
26
26
  before do
27
27
  allow(Models::Transaction).to receive(:new) { fake_transaction }
28
28
  end
@@ -3,17 +3,51 @@
3
3
  module Norma43
4
4
  module Models
5
5
  RSpec.describe Account do
6
+ it_behaves_like "a model"
7
+
6
8
  describe "#iban" do
7
- it { is_expected.to respond_to :iban }
9
+ it { is_expected.to respond_to(:iban) }
8
10
 
9
11
  context "with the example in the documentation" do
10
- it "delegates to SpanishIban to return a IBAN string" do
12
+ it "returns the correct IBAN string" do
11
13
  account = subject
12
- allow(Account::SpanishIban).to receive(:from_account).with(account).and_return("ES0000000000000000000123")
13
- expect(account.iban).to eq("ES0000000000000000000123")
14
- expect(Account::SpanishIban).to have_received(:from_account).with(account)
14
+ account.account_number = 1234567
15
+ account.bank_code = 81
16
+ account.branch_code = 54
17
+
18
+ expect(account.iban).to eq("ES5400810054180001234567")
15
19
  end
16
20
  end
21
+
22
+ context "with missing bank data" do
23
+ subject { described_class.new.iban }
24
+ it { is_expected.to be_nil }
25
+ end
26
+
27
+ context "with missing bank code" do
28
+ subject { described_class.new(bank_code: nil, branch_code: 1234, account_number: 1234).iban }
29
+ it { is_expected.to be_nil }
30
+ end
31
+
32
+ context "with missing branch code" do
33
+ subject { described_class.new(bank_code: 1234, branch_code: nil, account_number: 1234).iban }
34
+ it { is_expected.to be_nil }
35
+ end
36
+
37
+ context "with missing account number" do
38
+ subject { described_class.new(bank_code: 1234, branch_code: 1234, account_number: nil).iban }
39
+ it { is_expected.to be_nil }
40
+ end
41
+
42
+ context "with negative account number" do
43
+ subject { described_class.new(bank_code: 1234, branch_code: 1234, account_number: -1234).iban }
44
+ it { is_expected.to be_nil }
45
+ end
46
+
47
+ context "with an account number which is too large" do
48
+ subject { described_class.new(bank_code: 1234, branch_code: 1234, account_number: 1*10**10).iban }
49
+ it { is_expected.to be_nil }
50
+ end
17
51
  end
18
52
  end
19
53
  end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Norma43
4
+ module Models
5
+ RSpec.describe Document do
6
+ it_behaves_like "a model"
7
+
8
+ describe "#transaction_date" do
9
+ it { is_expected.to respond_to(:transaction_date) }
10
+
11
+ context "when there are no accounts" do
12
+ subject(:document) { described_class.new({ accounts: [] }) }
13
+
14
+ it { expect(document.transaction_date).to be_nil }
15
+ end
16
+
17
+ context "when there are accounts" do
18
+ subject(:document) { described_class.new({ accounts: [account] }) }
19
+
20
+ let(:account) {
21
+ Account.new(transactions: [
22
+ nil,
23
+ Transaction.new(transaction_date: nil),
24
+ Transaction.new(transaction_date: Date.parse("2024-01-23")),
25
+ ])
26
+ }
27
+
28
+ it "returns the date of the first account with a transaction date" do
29
+ expect(document.transaction_date).to eq(Date.parse("2024-01-23"))
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Norma43
4
+ module Models
5
+ RSpec.describe Transaction do
6
+ it_behaves_like "a model"
7
+
8
+ describe "#debit?" do
9
+ it { is_expected.to respond_to(:debit?) }
10
+
11
+ context "when amount code is code for debits" do
12
+ subject(:transaction) { described_class.new(amount_code: 1) }
13
+
14
+ it { expect(transaction.debit?).to be_truthy }
15
+ end
16
+
17
+ context "when amount code is code for credits" do
18
+ subject(:transaction) { described_class.new(amount_code: 2) }
19
+
20
+ it { expect(transaction.debit?).to be_falsey }
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
data/spec/norma43_spec.rb CHANGED
@@ -6,7 +6,7 @@ RSpec.describe Norma43 do
6
6
  describe "#parse" do
7
7
  it "returns the parser results" do
8
8
  text = "some total-in text"
9
- parser = double "Parser", result: "result"
9
+ parser = instance_double(Norma43::Parser, result: "result")
10
10
  expect(Norma43::Parser).to receive(:new).with(text) { parser }
11
11
 
12
12
  expect(Norma43.parse(text)).to eq "result"
data/spec/spec_helper.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "norma43"
4
+
3
5
  RSpec.configure do |config|
4
6
  config.order = :random
5
7
  Kernel.srand config.seed
@@ -11,6 +13,7 @@ RSpec.configure do |config|
11
13
  config.mock_with :rspec do |mocks|
12
14
  mocks.syntax = :expect
13
15
  mocks.verify_partial_doubles = true
16
+ mocks.verify_doubled_constant_names = true
14
17
  end
15
18
  end
16
19
 
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.shared_examples "a model" do
4
+ it { is_expected.to respond_to(:attributes) }
5
+ it { is_expected.to respond_to(:attributes=) }
6
+ it { is_expected.to respond_to(:to_h) }
7
+ it { is_expected.to respond_to(:to_hash) }
8
+
9
+ describe "#new" do
10
+ subject(:model) { described_class.new(attributes) }
11
+
12
+ context "when instantiated with an empty hash" do
13
+ let(:attributes) { {} }
14
+
15
+ it "accepts it without failing" do
16
+ expect { model }.not_to raise_error
17
+ end
18
+ end
19
+ end
20
+
21
+ describe "#attributes" do
22
+ subject { described_class.new.attributes }
23
+
24
+ it { is_expected.not_to be_empty }
25
+ it { is_expected.to respond_to(:each_pair) }
26
+ it { is_expected.to respond_to(:to_h) }
27
+ it { is_expected.to respond_to(:to_hash) }
28
+ it { is_expected.to respond_to(:keys) }
29
+
30
+ describe "#attributes.keys" do
31
+ subject { super().keys }
32
+
33
+ it { is_expected.not_to be_empty }
34
+ it { is_expected.to all(be_a(Symbol)) }
35
+ end
36
+ end
37
+
38
+ describe "#attributes=" do
39
+ subject { described_class.new.attributes=(attributes) }
40
+
41
+ context "when passed an empty hash" do
42
+ let(:attributes) { {} }
43
+
44
+ it "accepts it without failing" do
45
+ expect { subject }.not_to raise_error
46
+ end
47
+ end
48
+
49
+ context "when passed a hash with unknown attribute names" do
50
+ let(:attributes) { { potato: nil } }
51
+
52
+ it "accepts it without failing" do
53
+ expect { subject }.not_to raise_error
54
+ end
55
+
56
+ it "does not set any new method" do
57
+ expect { subject.potato }.to raise_error(NoMethodError, /undefined method.+potato/i)
58
+ end
59
+ end
60
+ end
61
+ end
metadata CHANGED
@@ -1,29 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: norma43_parser
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.0
4
+ version: 4.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sequra engineering
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-06-19 00:00:00.000000000 Z
11
+ date: 2025-03-25 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: virtus
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '1.0'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: '1.0'
27
13
  - !ruby/object:Gem::Dependency
28
14
  name: zeitwerk
29
15
  requirement: !ruby/object:Gem::Requirement
@@ -66,7 +52,7 @@ dependencies:
66
52
  - - "~>"
67
53
  - !ruby/object:Gem::Version
68
54
  version: '3.9'
69
- description:
55
+ description:
70
56
  email:
71
57
  - dev@sequra.es
72
58
  executables: []
@@ -75,8 +61,10 @@ extra_rdoc_files: []
75
61
  files:
76
62
  - ".github/ISSUE_TEMPLATE.md"
77
63
  - ".github/PULL_REQUEST_TEMPLATE.md"
64
+ - ".github/dependabot.yml"
78
65
  - ".github/workflows/ci.yml"
79
66
  - ".github/workflows/release.yml"
67
+ - ".rspec"
80
68
  - ".rubocop.yml"
81
69
  - CHANGELOG.md
82
70
  - Gemfile
@@ -104,6 +92,7 @@ files:
104
92
  - lib/norma43/models/additional_currency.rb
105
93
  - lib/norma43/models/additional_item.rb
106
94
  - lib/norma43/models/document.rb
95
+ - lib/norma43/models/mixins/attributes_assignment.rb
107
96
  - lib/norma43/models/transaction.rb
108
97
  - lib/norma43/parser.rb
109
98
  - lib/norma43/utils/contexts.rb
@@ -112,7 +101,9 @@ files:
112
101
  - lib/norma43/version.rb
113
102
  - norma43_parser.gemspec
114
103
  - spec/example1_parse_spec.rb
104
+ - spec/example2_parse_spec.rb
115
105
  - spec/fixtures/example1.n43
106
+ - spec/fixtures/example2.n43
116
107
  - spec/norma43/line_parsers/account_end_spec.rb
117
108
  - spec/norma43/line_parsers/account_start_spec.rb
118
109
  - spec/norma43/line_parsers/additional_currency_spec.rb
@@ -123,21 +114,24 @@ files:
123
114
  - spec/norma43/line_processors/account_end_spec.rb
124
115
  - spec/norma43/line_processors/account_start_spec.rb
125
116
  - spec/norma43/line_processors/additional_currency_spec.rb
126
- - spec/norma43/line_processors/additional_items_spec.rb
117
+ - spec/norma43/line_processors/additional_item_spec.rb
127
118
  - spec/norma43/line_processors/document_end_spec.rb
128
119
  - spec/norma43/line_processors/document_start_spec.rb
129
120
  - spec/norma43/line_processors/transaction_spec.rb
130
121
  - spec/norma43/models/account/spanish_iban_spec.rb
131
122
  - spec/norma43/models/account_spec.rb
123
+ - spec/norma43/models/document_spec.rb
124
+ - spec/norma43/models/transaction_spec.rb
132
125
  - spec/norma43/parser_spec.rb
133
126
  - spec/norma43_spec.rb
134
127
  - spec/spec_helper.rb
128
+ - spec/support/shared_examples_for_models.rb
135
129
  - spec/support/shared_examples_for_values_line_parsers.rb
136
130
  homepage: https://github.com/sequra/norma43_parser
137
131
  licenses:
138
132
  - MIT
139
133
  metadata: {}
140
- post_install_message:
134
+ post_install_message:
141
135
  rdoc_options: []
142
136
  require_paths:
143
137
  - lib
@@ -153,12 +147,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
153
147
  version: '0'
154
148
  requirements: []
155
149
  rubygems_version: 3.4.19
156
- signing_key:
150
+ signing_key:
157
151
  specification_version: 4
158
152
  summary: Parses banks transactions files specified in rule 43
159
153
  test_files:
160
154
  - spec/example1_parse_spec.rb
155
+ - spec/example2_parse_spec.rb
161
156
  - spec/fixtures/example1.n43
157
+ - spec/fixtures/example2.n43
162
158
  - spec/norma43/line_parsers/account_end_spec.rb
163
159
  - spec/norma43/line_parsers/account_start_spec.rb
164
160
  - spec/norma43/line_parsers/additional_currency_spec.rb
@@ -169,13 +165,16 @@ test_files:
169
165
  - spec/norma43/line_processors/account_end_spec.rb
170
166
  - spec/norma43/line_processors/account_start_spec.rb
171
167
  - spec/norma43/line_processors/additional_currency_spec.rb
172
- - spec/norma43/line_processors/additional_items_spec.rb
168
+ - spec/norma43/line_processors/additional_item_spec.rb
173
169
  - spec/norma43/line_processors/document_end_spec.rb
174
170
  - spec/norma43/line_processors/document_start_spec.rb
175
171
  - spec/norma43/line_processors/transaction_spec.rb
176
172
  - spec/norma43/models/account/spanish_iban_spec.rb
177
173
  - spec/norma43/models/account_spec.rb
174
+ - spec/norma43/models/document_spec.rb
175
+ - spec/norma43/models/transaction_spec.rb
178
176
  - spec/norma43/parser_spec.rb
179
177
  - spec/norma43_spec.rb
180
178
  - spec/spec_helper.rb
179
+ - spec/support/shared_examples_for_models.rb
181
180
  - spec/support/shared_examples_for_values_line_parsers.rb