wire_client 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +35 -0
  3. data/.editorconfig +13 -0
  4. data/.gitignore +11 -0
  5. data/.ruby-version +1 -0
  6. data/.tool-versions +1 -0
  7. data/.tool-versions-e +1 -0
  8. data/Gemfile +4 -0
  9. data/LICENSE +22 -0
  10. data/README.md +46 -0
  11. data/Rakefile +27 -0
  12. data/bin/setup +8 -0
  13. data/lib/wire_client/account/account.rb +59 -0
  14. data/lib/wire_client/account/creditor_account.rb +7 -0
  15. data/lib/wire_client/account/debitor_account.rb +7 -0
  16. data/lib/wire_client/base/constants.rb +61 -0
  17. data/lib/wire_client/base/converters.rb +46 -0
  18. data/lib/wire_client/base/invalid_wire_transaction_error.rb +4 -0
  19. data/lib/wire_client/base/invalid_wire_transaction_type_error.rb +3 -0
  20. data/lib/wire_client/base/validators.rb +95 -0
  21. data/lib/wire_client/messages/credit_transfer.rb +112 -0
  22. data/lib/wire_client/messages/direct_debit.rb +148 -0
  23. data/lib/wire_client/messages/message.rb +215 -0
  24. data/lib/wire_client/providers/base/wire_batch.rb +120 -0
  25. data/lib/wire_client/providers/sftp/sftp_provider.rb +39 -0
  26. data/lib/wire_client/providers/sftp/wire_batch.rb +39 -0
  27. data/lib/wire_client/transaction/credit_transfer_transaction.rb +20 -0
  28. data/lib/wire_client/transaction/direct_debit_transaction.rb +49 -0
  29. data/lib/wire_client/transaction/transaction.rb +87 -0
  30. data/lib/wire_client/version.rb +4 -0
  31. data/lib/wire_client.rb +36 -0
  32. data/schemas/.gitattributes +1 -0
  33. data/schemas/pain.001.001.03.xsd +921 -0
  34. data/schemas/pain.008.001.02.xsd +879 -0
  35. data/wire_client.gemspec +66 -0
  36. metadata +375 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d23672dd2a990aa5ca2d19ce40540fbbe2f950cab218fc900542b33ef3e3ed8b
4
+ data.tar.gz: 875b4cd7eec9f842c4f9839ad6d9e454006b4e73c4a5050fed7647a9cb9ac8b0
5
+ SHA512:
6
+ metadata.gz: 9232a3b8bfb6ce923ceb8a089d2103aee42232ba88c9fe84f88f5b7e8dc13ffafcf15e36f2eb80672707aa380542e5ffbef78e373ef5a712cd3946a24aff1954
7
+ data.tar.gz: 8347c0487adc4c0697e60432bd936d70c56fc6559a5e790fe429be0605cf42c1ba44af12420305dd77ffa3ac9ecbffc7a752568ecd18c211337eccaa9c0767b6
data/.codeclimate.yml ADDED
@@ -0,0 +1,35 @@
1
+ prepare:
2
+ fetch:
3
+ - url: "https://raw.githubusercontent.com/ForwardFinancing/code_styles/master/rubocop.yml"
4
+ path: ".rubocop.yml"
5
+
6
+ - url: "https://raw.githubusercontent.com/ForwardFinancing/code_styles/master/stylelintrc"
7
+ path: ".stylelintrc"
8
+ engines:
9
+ bundler-audit:
10
+ enabled: false # somehow it is failing in CodeClimate's builds
11
+ fixme:
12
+ enabled: true
13
+ rubocop:
14
+ enabled: true
15
+ file: ".rubocop.yml"
16
+ channel: rubocop-0-48
17
+ checks:
18
+ Rubocop/Style/RegexpLiteral:
19
+ enabled: false
20
+ stylelint:
21
+ enabled: true
22
+ duplication:
23
+ enabled: true
24
+ config:
25
+ languages:
26
+ ruby:
27
+ mass_threshold: 20
28
+ ratings:
29
+ paths:
30
+ - "Gemfile.lock"
31
+ - "**.rb"
32
+ exclude_paths:
33
+ - "bin"
34
+ - "schemas"
35
+ - "test"
data/.editorconfig ADDED
@@ -0,0 +1,13 @@
1
+ # EditorConfig is awesome: http://EditorConfig.org
2
+
3
+ root = true
4
+
5
+ [*]
6
+ indent_style = space
7
+ indent_size = 2
8
+ charset = utf-8
9
+ end_of_line = lf
10
+ insert_final_newline = true
11
+
12
+ [*.xsd]
13
+ indent_size = 4
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ .DS_Store
11
+ *.gem
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.5.0
data/.tool-versions ADDED
@@ -0,0 +1 @@
1
+ ruby 2.5.0
data/.tool-versions-e ADDED
@@ -0,0 +1 @@
1
+ ruby 2.5.0
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in wire_client.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2018-present Forward Financing LLC
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,46 @@
1
+ [![Codeship Status for ForwardFinancing/wire_client](https://app.codeship.com/projects/80539010-e9b6-0135-7649-4a1e2141a6e6/status?branch=master)](https://app.codeship.com/projects/269959)
2
+ [![Code Climate](https://api.codeclimate.com/v1/badges/055840fb4eaf9e44ec80/maintainability)](https://codeclimate.com/repos/5a6a6058396682027c0028f6/maintainability)
3
+ [![Test Coverage](https://api.codeclimate.com/v1/badges/055840fb4eaf9e44ec80/test_coverage)](https://codeclimate.com/repos/5a6a6058396682027c0028f6/test_coverage)
4
+
5
+ # WireClient
6
+
7
+ > Implementation of ISO 20022 payment initiation (pain) messages and bank providers for wire transfers
8
+
9
+ ## Overview
10
+
11
+ The WireClient gem provides a common interface for working with a variety of Wire Transfer providers
12
+ and building ISO 20022 payment initiation (pain) messages — currently, only `pain.001.001.03`
13
+ and `pain.008.001.02` messages are supported.
14
+
15
+ This is a fork of the [`sepa_king`](https://github.com/salesking/sepa_king) Ruby gem. Our main purpose
16
+ is to create a flexible solution appliable to the American financial system.
17
+
18
+ ## Installation
19
+
20
+ Add this line to your application's Gemfile:
21
+
22
+ ```ruby
23
+ gem 'wire_client'
24
+ ```
25
+
26
+ And then execute:
27
+
28
+ ```sh
29
+ $ bundle install
30
+ ```
31
+
32
+ Or install it yourself:
33
+
34
+ ```sh
35
+ $ gem install wire_client
36
+ ```
37
+
38
+ ## Development
39
+
40
+ After checking out the repo, run `bin/setup` to install dependencies.
41
+
42
+ Then, run `bundle exec rake test` to run the tests.
43
+
44
+ ## Contributing
45
+
46
+ Bug reports and pull requests are welcome on GitHub at https://github.com/ForwardFinancing/wire_client.
data/Rakefile ADDED
@@ -0,0 +1,27 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ lib = File.expand_path('../lib', __FILE__)
5
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
+ require 'wire_client/version'
7
+
8
+ GEM = 'wire_client'
9
+ VERSION = WireClient::VERSION
10
+
11
+ Rake::TestTask.new(:test) do |t|
12
+ t.libs << 'test'
13
+ t.libs << 'lib'
14
+ t.test_files = FileList['test/**/*_test.rb']
15
+ end
16
+
17
+ desc 'Build the sequel-seed gem'
18
+ task :build do |p|
19
+ sh %{#{FileUtils::RUBY} -S gem build #{GEM}.gemspec}
20
+ end
21
+
22
+ desc 'Release the sequel-seed gem to rubygems.org'
23
+ task release: :build do
24
+ sh %{#{FileUtils::RUBY} -S gem push ./#{GEM}-#{VERSION}.gem}
25
+ end
26
+
27
+ task default: :test
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,59 @@
1
+ require_relative '../base/converters'
2
+ require_relative '../base/validators'
3
+
4
+ module WireClient
5
+ class Account
6
+ include ActiveModel::Validations
7
+ extend Converter
8
+
9
+ attr_accessor :name,
10
+ :iban,
11
+ :bic,
12
+ :account_number,
13
+ :wire_routing_number,
14
+ :clear_system_code,
15
+ :schema_code,
16
+ :identifier,
17
+ :country,
18
+ :country_subdivision,
19
+ :charge_bearer,
20
+ :currency
21
+
22
+ convert :name, to: :text
23
+ validates_length_of :name, within: 1..70
24
+ validates_with CurrencyValidator,
25
+ CountryValidator,
26
+ CountrySubdivisionValidator,
27
+ CreditorIdentifierValidator,
28
+ BICValidator,
29
+ IBANValidator,
30
+ message: "%{value} is invalid"
31
+
32
+ def initialize(attributes = {})
33
+ attributes.each do |name, value|
34
+ public_send("#{name}=", value)
35
+ end
36
+
37
+ @currency ||= 'USD'
38
+ @country ||= 'US'
39
+ @country_subdivision ||= 'MA' if self.country == 'US'
40
+ @schema_code ||= 'CUST'
41
+ @clear_system_code ||= 'USABA'
42
+ custom_defaults if self.respond_to? :custom_defaults
43
+ end
44
+
45
+ def country_subdivision_abbr
46
+ if @country == 'US' && !@country_subdivision.match(/\A[A-Z]{2,2}\z/)
47
+ return US_STATES[@country_subdivision]
48
+ end
49
+ @country_subdivision
50
+ end
51
+
52
+ def country_subdivision_name
53
+ if @country == 'US' && @country_subdivision.match(/\A[A-Z]{2,2}\z/)
54
+ return US_STATES.key(@country_subdivision)
55
+ end
56
+ @country_subdivision
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,7 @@
1
+ module WireClient
2
+ class CreditorAccount < Account
3
+ def custom_defaults
4
+ @charge_bearer ||= 'CRED'
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module WireClient
2
+ class DebtorAccount < Account
3
+ def custom_defaults
4
+ @charge_bearer ||= 'DEBT'
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,61 @@
1
+ module WireClient
2
+ PAIN_008_001_02 = 'pain.008.001.02'
3
+ PAIN_001_001_03 = 'pain.001.001.03'
4
+ US_STATES = {
5
+ "Alabama" => "AL",
6
+ "Alaska" => "AK",
7
+ "Arizona" => "AZ",
8
+ "Arkansas" => "AR",
9
+ "California" => "CA",
10
+ "Colorado" => "CO",
11
+ "Connecticut" => "CT",
12
+ "Delaware" => "DE",
13
+ "District of Columbia" => "DC",
14
+ "Florida" => "FL",
15
+ "Georgia" => "GA",
16
+ "Hawaii" => "HI",
17
+ "Idaho" => "ID",
18
+ "Illinois" => "IL",
19
+ "Indiana" => "IN",
20
+ "Iowa" => "IA",
21
+ "Kansas" => "KS",
22
+ "Kentucky" => "KY",
23
+ "Louisiana" => "LA",
24
+ "Maine" => "ME",
25
+ "Maryland" => "MD",
26
+ "Massachusetts" => "MA",
27
+ "Michigan" => "MI",
28
+ "Minnesota" => "MN",
29
+ "Mississippi" => "MS",
30
+ "Missouri" => "MO",
31
+ "Montana" => "MT",
32
+ "Nebraska" => "NE",
33
+ "Nevada" => "NV",
34
+ "New Hampshire" => "NH",
35
+ "New Jersey" => "NJ",
36
+ "New Mexico" => "NM",
37
+ "New York" => "NY",
38
+ "North Carolina" => "NC",
39
+ "North Dakota" => "ND",
40
+ "Ohio" => "OH",
41
+ "Oklahoma" => "OK",
42
+ "Oregon" => "OR",
43
+ "Pennsylvania" => "PA",
44
+ "Rhode Island" => "RI",
45
+ "South Carolina" => "SC",
46
+ "South Dakota" => "SD",
47
+ "Tennessee" => "TN",
48
+ "Texas" => "TX",
49
+ "Utah" => "UT",
50
+ "Vermont" => "VT",
51
+ "Virginia" => "VA",
52
+ "Washington" => "WA",
53
+ "West Virginia" => "WV",
54
+ "Wisconsin" => "WI",
55
+ "Wyoming" => "WY"
56
+ }
57
+ SEQUENCE_TYPES = %w(FRST OOFF RCUR FNAL)
58
+ LOCAL_INSTRUMENTS = %w(CORE COR1 B2B)
59
+ SERVICE_PRIORITY_TYPES = %w(HIGH NORM)
60
+ SERVICE_LEVEL_TYPES = %w(BKTR URGP URNS NURG SEPA)
61
+ end
@@ -0,0 +1,46 @@
1
+ module WireClient
2
+ module Converter
3
+ def convert(*attributes, options)
4
+ include InstanceMethods
5
+
6
+ method_name = "convert_#{options[:to]}"
7
+ unless InstanceMethods.method_defined?(method_name)
8
+ raise ArgumentError, "Converter '#{options[:to]}' does not exist!"
9
+ end
10
+
11
+ attributes.each do |attribute|
12
+ define_method "#{attribute}=" do |value|
13
+ instance_variable_set("@#{attribute}", send(method_name, value))
14
+ end
15
+ end
16
+ end
17
+
18
+ module InstanceMethods
19
+ def convert_text(value)
20
+ return unless value.present?
21
+ value.to_s.
22
+ # Replace some special characters described as "Best practices"
23
+ # in Chapter 6.2 of this document:
24
+ # http://www.europeanpaymentscouncil.eu/index.cfm/knowledge-bank/epc-documents/sepa-requirements-for-an-extended-character-set-unicode-subset-best-practices/
25
+ tr('€', 'E').
26
+ gsub('@', '(at)').
27
+ tr('_', '-').
28
+
29
+ # Replace linebreaks by spaces
30
+ gsub(/\n+/, ' ').
31
+
32
+ # Remove all invalid characters
33
+ gsub(/[^a-zA-Z0-9ÄÖÜäöüß&*$%\ \'\:\?\,\-\(\+\.\)\/]/, '').
34
+
35
+ # Remove leading and trailing spaces
36
+ strip
37
+ end
38
+
39
+ def convert_decimal(value)
40
+ return unless value.present?
41
+ value = BigDecimal(value.to_f&.to_s)
42
+ value.round(2) if value&.finite? && value.positive?
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,4 @@
1
+ # To be raised when an Wire transfer transaction cannot be
2
+ # sent because it is invalid
3
+ class InvalidWireTransactionError < RuntimeError
4
+ end
@@ -0,0 +1,3 @@
1
+ # To be raised when an Wire transfer transaction type is invalid
2
+ class InvalidWireTransactionTypeError < RuntimeError
3
+ end
@@ -0,0 +1,95 @@
1
+ module WireClient
2
+ class BaseValidator < ActiveModel::Validator
3
+ class_attribute :default_field_name
4
+
5
+ def extract_field_name_value(record)
6
+ field_name = options[:field_name] || self.class.default_field_name
7
+ value = record.send(field_name).to_s
8
+
9
+ [field_name, value]
10
+ end
11
+ end
12
+
13
+ class RegexBasedValidators < BaseValidator
14
+ def validate(record)
15
+ field_name, value = extract_field_name_value(record)
16
+
17
+ unless value.match(self.class::REGEX)
18
+ record.errors.add(field_name, :invalid, message: options[:message])
19
+ end
20
+ end
21
+ end
22
+
23
+ class CurrencyValidator < RegexBasedValidators
24
+ REGEX = /\A[A-Z]{3,3}\z/
25
+
26
+ self.default_field_name = :currency
27
+ end
28
+
29
+ class CountryValidator < RegexBasedValidators
30
+ REGEX = /\A[A-Z]{2,2}\z/
31
+
32
+ self.default_field_name = :country
33
+ end
34
+
35
+ class CreditorIdentifierValidator < RegexBasedValidators
36
+ REGEX = /\A[a-zA-Z0-9]{1,35}\z/
37
+
38
+ self.default_field_name = :identifier
39
+ end
40
+
41
+ class MandateIdentifierValidator < RegexBasedValidators
42
+ REGEX = /\A([A-Za-z0-9]|[\+|\?|\/|\-|\:|\(|\)|\.|\,|\']){1,35}\z/
43
+
44
+ self.default_field_name = :mandate_id
45
+ end
46
+
47
+ class CountrySubdivisionValidator < BaseValidator
48
+ self.default_field_name = :country_subdivision
49
+
50
+ def validate(record)
51
+ field_name, value = extract_field_name_value(record)
52
+ country = record.send(options[:country] || :country).to_s
53
+
54
+ if country == 'US'
55
+ unless US_STATES.key?(value) || US_STATES.value?(value)
56
+ record.errors.add(field_name, :invalid, message: options[:message])
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ class IBANValidator < BaseValidator
63
+ # IBAN2007Identifier (taken from schema)
64
+ REGEX = /\A[A-Z]{2,2}[0-9]{2,2}[a-zA-Z0-9]{1,30}\z/
65
+
66
+ self.default_field_name = :iban
67
+
68
+ def validate(record)
69
+ field_name, value = extract_field_name_value(record)
70
+
71
+ if value.present?
72
+ unless IBANTools::IBAN.valid?(value) && value.match(REGEX)
73
+ record.errors.add(field_name, :invalid, message: options[:message])
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ class BICValidator < BaseValidator
80
+ # AnyBICIdentifier (taken from schema)
81
+ REGEX = /\A[A-Z]{6,6}[A-Z2-9][A-NP-Z0-9]([A-Z0-9]{3,3}){0,1}\z/
82
+
83
+ self.default_field_name = :bic
84
+
85
+ def validate(record)
86
+ field_name, value = extract_field_name_value(record)
87
+
88
+ if value.present?
89
+ unless value.match(REGEX)
90
+ record.errors.add(field_name, :invalid, message: options[:message])
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,112 @@
1
+ require_relative './message'
2
+ require_relative '../transaction/credit_transfer_transaction'
3
+
4
+ module WireClient
5
+ class CreditTransfer < Message
6
+ self.account_class = DebtorAccount
7
+ self.transaction_class = CreditTransferTransaction
8
+ self.xml_main_tag = 'CstmrCdtTrfInitn'
9
+ self.known_schemas = [WireClient::PAIN_001_001_03]
10
+
11
+ private
12
+
13
+ # Find groups of transactions which share the same values for
14
+ # selected attributes
15
+ def transaction_group(transaction)
16
+ {
17
+ requested_date: transaction.requested_date,
18
+ batch_booking: transaction.batch_booking,
19
+ service_priority: transaction.service_priority,
20
+ service_level: transaction.service_level
21
+ }
22
+ end
23
+
24
+ def build_payment_information(builder)
25
+ # Build a PmtInf block for every group of transactions
26
+ grouped_transactions.each do |group, transactions|
27
+ # All transactions with the same requested_date are placed into the
28
+ # same PmtInf block
29
+ builder.PmtInf do
30
+ builder.PmtInfId(payment_information_identification(group))
31
+ builder.PmtMtd('TRF')
32
+ builder.NbOfTxs(transactions.length)
33
+ builder.PmtTpInf do
34
+ builder.InstrPrty(group[:service_priority])
35
+ builder.SvcLvl do
36
+ builder.Cd(group[:service_level])
37
+ end
38
+ end
39
+ builder.ReqdExctnDt(group[:requested_date].iso8601)
40
+ builder.BtchBookg(group[:batch_booking])
41
+ builder.CtrlSum('%.2f' % amount_total(transactions))
42
+ builder.Dbtr do
43
+ builder.Nm(account.name)
44
+ builder.PstlAdr do
45
+ builder.CtrySubDvsn(account.country_subdivision_abbr)
46
+ builder.Ctry(account.country)
47
+ end
48
+ end
49
+ builder.DbtrAcct do
50
+ account_id(builder, account)
51
+ end
52
+ builder.DbtrAgt do
53
+ builder.FinInstnId do
54
+ account_agent_id(builder, account)
55
+ builder.PstlAdr do
56
+ builder.Ctry(account.country)
57
+ end
58
+ end
59
+ end
60
+
61
+ if account.charge_bearer
62
+ builder.ChrgBr(account.charge_bearer)
63
+ end
64
+
65
+ transactions.each do |transaction|
66
+ build_transaction(builder, transaction)
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ def build_transaction(builder, transaction)
73
+ builder.CdtTrfTxInf do
74
+ builder.PmtId do
75
+ if transaction.instruction.present?
76
+ builder.InstrId(transaction.instruction)
77
+ end
78
+ builder.EndToEndId(transaction.reference)
79
+ end
80
+ builder.Amt do
81
+ builder.InstdAmt(
82
+ '%.2f' % transaction.amount,
83
+ Ccy: transaction.currency
84
+ )
85
+ end
86
+ builder.CdtrAgt do
87
+ builder.FinInstnId do
88
+ transaction_agent_id(builder, transaction)
89
+ builder.Nm(transaction.agent_name)
90
+ builder.PstlAdr do
91
+ builder.Ctry(transaction.country)
92
+ end
93
+ end
94
+ end
95
+ builder.Cdtr do
96
+ builder.Nm(transaction.name)
97
+ builder.PstlAdr do
98
+ builder.Ctry(transaction.country)
99
+ end
100
+ end
101
+ builder.CdtrAcct do
102
+ transaction_account_id(builder, transaction)
103
+ end
104
+ if transaction.remittance_information
105
+ builder.RmtInf do
106
+ builder.Ustrd(transaction.remittance_information)
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end