sepa_king 0.11.0 → 0.11.1

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: 770829019aef0e261d784bff74bba479c2bc629d5b5cd29fd8ed5987dd3f2a36
4
- data.tar.gz: 2929b12cc5c35646e0991f1747e8d329b1d88293bad2ed0d1ddc0f4c0c90522c
3
+ metadata.gz: 90e4af1e7d2d11456cc48e339006078182b067cd6a2c49c3314ba296fc295fc3
4
+ data.tar.gz: f224fa0fcd711759b1b56baf53c78b1bf5b8f90f1828ced8a6ce7dc21ad80e0c
5
5
  SHA512:
6
- metadata.gz: f78107df5faad7d6cd0f71ceeefcc8ed385aba409aab707c6d7552530d425cdad7d33fd5f4e9087236ea68d57c1b35888a29f9ffc176802c36e7c459afa2a6b0
7
- data.tar.gz: 2d42905a7c00c9d5daa8fd64a4eaa6018c3210b542d935a962beab83e035b6ec227501c5bfa847645382381dbca5c953eea68c7df547dab53454af92083cb4f6
6
+ metadata.gz: 9d8c42503c8bc1e42669d624376b74b741797824b3115e2314152f7340ab2d5740850db08bc6c7cf81afa6bfd4fba66237e41003c00803f7c7adae1215a437ff
7
+ data.tar.gz: 21174103026c3987e78a71703124bb41be4fd8e19098c02a519ed8cc8d9524da593295531557ae4104a3fcc2f7a40ababde3bac23042f9b2735aa031556427c0
data/README.md CHANGED
@@ -130,11 +130,17 @@ sdd.add_transaction(
130
130
  creditor_identifier: 'NL53ZZZ091734220000'
131
131
  )
132
132
 
133
- # OPTIONAL: Specify the country & address of the debtor (REQUIRED for SEPA debits outside of EU)
133
+ # OPTIONAL: Specify the country & address of the debtor (REQUIRED for SEPA debits outside of EU. The individually required fields depend on the target country)
134
134
  debtor_address: SEPA::DebtorAddress.new(
135
135
  country_code: 'CH',
136
+ # Not required if individual fields are used
136
137
  address_line1: 'Mustergasse 123a',
137
138
  address_line2: '1234 Musterstadt'
139
+ # Not required if address_line1 and address_line2 are used
140
+ street_name: 'Mustergasse',
141
+ building_number: '123a',
142
+ post_code: '1234',
143
+ town_name: 'Musterstadt'
138
144
  )
139
145
  )
140
146
  sdd.add_transaction ...
@@ -210,6 +216,23 @@ sct.add_transaction(
210
216
  # 'SEPA' ("SEPA-Zahlung")
211
217
  # 'URGP' ("Taggleiche Eilüberweisung")
212
218
  service_level: 'URGP'
219
+
220
+ # OPTIONAL: Unstructured information to indicate the purpose of the payment
221
+ # String, max. 4 char
222
+ category_purpose: 'SALA',
223
+
224
+ # OPTIONAL: Specify the country & address of the creditor (REQUIRED for SEPA debits outside of EU. The individually required fields depend on the target country)
225
+ creditor_address: SEPA::CreditorAddress.new(
226
+ country_code: 'CH',
227
+ # Not required if individual fields are used
228
+ address_line1: 'Mustergasse 123a',
229
+ address_line2: '1234 Musterstadt'
230
+ # Not required if address_line1 and address_line2 are used
231
+ street_name: 'Mustergasse',
232
+ building_number: '123a',
233
+ post_code: '1234',
234
+ town_name: 'Musterstadt'
235
+ )
213
236
  )
214
237
  sct.add_transaction ...
215
238
 
data/bin/rake ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'rake' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require "pathname"
12
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13
+ Pathname.new(__FILE__).realpath)
14
+
15
+ bundle_binstub = File.expand_path("../bundle", __FILE__)
16
+
17
+ if File.file?(bundle_binstub)
18
+ if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19
+ load(bundle_binstub)
20
+ else
21
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23
+ end
24
+ end
25
+
26
+ require "rubygems"
27
+ require "bundler/setup"
28
+
29
+ load Gem.bin_path("rake", "rake")
@@ -0,0 +1,37 @@
1
+ # encoding: utf-8
2
+ module SEPA
3
+ class CreditorAddress
4
+ include ActiveModel::Validations
5
+ extend Converter
6
+
7
+ attr_accessor :street_name,
8
+ :building_number,
9
+ :post_code,
10
+ :town_name,
11
+ :country_code,
12
+ :address_line1,
13
+ :address_line2
14
+
15
+ convert :street_name, to: :text
16
+ convert :building_number, to: :text
17
+ convert :post_code, to: :text
18
+ convert :town_name, to: :text
19
+ convert :country_code, to: :text
20
+ convert :address_line1, to: :text
21
+ convert :address_line2, to: :text
22
+
23
+ validates_length_of :street_name, maximum: 70
24
+ validates_length_of :building_number, maximum: 16
25
+ validates_length_of :post_code, maximum: 16
26
+ validates_length_of :town_name, maximum: 35
27
+ validates_length_of :country_code, is: 2
28
+ validates_length_of :address_line1, maximum: 70
29
+ validates_length_of :address_line2, maximum: 70
30
+
31
+ def initialize(attributes = {})
32
+ attributes.each do |name, value|
33
+ public_send("#{name}=", value)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -4,15 +4,29 @@ module SEPA
4
4
  include ActiveModel::Validations
5
5
  extend Converter
6
6
 
7
- attr_accessor :country_code, :address_line1, :address_line2
7
+ attr_accessor :street_name,
8
+ :building_number,
9
+ :post_code,
10
+ :town_name,
11
+ :country_code,
12
+ :address_line1,
13
+ :address_line2
8
14
 
9
- convert :country_code, to: :text
10
- convert :address_line1, to: :text
11
- convert :address_line2, to: :text
15
+ convert :street_name, to: :text
16
+ convert :building_number, to: :text
17
+ convert :post_code, to: :text
18
+ convert :town_name, to: :text
19
+ convert :country_code, to: :text
20
+ convert :address_line1, to: :text
21
+ convert :address_line2, to: :text
12
22
 
13
- validates_length_of :country_code, is: 2
14
- validates_length_of :address_line1, maximum: 70
15
- validates_length_of :address_line2, maximum: 70
23
+ validates_length_of :street_name, maximum: 70
24
+ validates_length_of :building_number, maximum: 16
25
+ validates_length_of :post_code, maximum: 16
26
+ validates_length_of :town_name, maximum: 35
27
+ validates_length_of :country_code, is: 2
28
+ validates_length_of :address_line1, maximum: 70
29
+ validates_length_of :address_line2, maximum: 70
16
30
 
17
31
  def initialize(attributes = {})
18
32
  attributes.each do |name, value|
@@ -0,0 +1,4 @@
1
+ module SEPA
2
+ class Error < RuntimeError
3
+ end
4
+ end
@@ -12,7 +12,8 @@ module SEPA
12
12
  def transaction_group(transaction)
13
13
  { requested_date: transaction.requested_date,
14
14
  batch_booking: transaction.batch_booking,
15
- service_level: transaction.service_level
15
+ service_level: transaction.service_level,
16
+ category_purpose: transaction.category_purpose
16
17
  }
17
18
  end
18
19
 
@@ -27,8 +28,15 @@ module SEPA
27
28
  builder.NbOfTxs(transactions.length)
28
29
  builder.CtrlSum('%.2f' % amount_total(transactions))
29
30
  builder.PmtTpInf do
30
- builder.SvcLvl do
31
- builder.Cd(group[:service_level])
31
+ if group[:service_level]
32
+ builder.SvcLvl do
33
+ builder.Cd(group[:service_level])
34
+ end
35
+ end
36
+ if group[:category_purpose]
37
+ builder.CtgyPurp do
38
+ builder.Cd(group[:category_purpose])
39
+ end
32
40
  end
33
41
  end
34
42
  builder.ReqdExctnDt(group[:requested_date].iso8601)
@@ -80,6 +88,44 @@ module SEPA
80
88
  end
81
89
  builder.Cdtr do
82
90
  builder.Nm(transaction.name)
91
+ if transaction.creditor_address
92
+ builder.PstlAdr do
93
+ # Only set the fields that are actually provided.
94
+ # StrtNm, BldgNb, PstCd, TwnNm provide a structured address
95
+ # separated into its individual fields.
96
+ # AdrLine provides the address in free format text.
97
+ # Both are currently allowed and the actual preference depends on the bank.
98
+ # Also the fields that are required legally may vary depending on the country
99
+ # or change over time.
100
+ if transaction.creditor_address.street_name
101
+ builder.StrtNm transaction.creditor_address.street_name
102
+ end
103
+
104
+ if transaction.creditor_address.building_number
105
+ builder.BldgNb transaction.creditor_address.building_number
106
+ end
107
+
108
+ if transaction.creditor_address.post_code
109
+ builder.PstCd transaction.creditor_address.post_code
110
+ end
111
+
112
+ if transaction.creditor_address.town_name
113
+ builder.TwnNm transaction.creditor_address.town_name
114
+ end
115
+
116
+ if transaction.creditor_address.country_code
117
+ builder.Ctry transaction.creditor_address.country_code
118
+ end
119
+
120
+ if transaction.creditor_address.address_line1
121
+ builder.AdrLine transaction.creditor_address.address_line1
122
+ end
123
+
124
+ if transaction.creditor_address.address_line2
125
+ builder.AdrLine transaction.creditor_address.address_line2
126
+ end
127
+ end
128
+ end
83
129
  end
84
130
  builder.CdtrAcct do
85
131
  builder.Id do
@@ -111,6 +111,9 @@ module SEPA
111
111
  builder.PrvtId do
112
112
  builder.Othr do
113
113
  builder.Id(transaction.original_creditor_account.creditor_identifier)
114
+ builder.SchmeNm do
115
+ builder.Prtry('SEPA')
116
+ end
114
117
  end
115
118
  end
116
119
  end
@@ -151,9 +154,40 @@ module SEPA
151
154
  builder.Nm(transaction.name)
152
155
  if transaction.debtor_address
153
156
  builder.PstlAdr do
154
- builder.Ctry transaction.debtor_address.country_code
155
- builder.AdrLine transaction.debtor_address.address_line1
156
- builder.AdrLine transaction.debtor_address.address_line2
157
+ # Only set the fields that are actually provided.
158
+ # StrtNm, BldgNb, PstCd, TwnNm provide a structured address
159
+ # separated into its individual fields.
160
+ # AdrLine provides the address in free format text.
161
+ # Both are currently allowed and the actual preference depends on the bank.
162
+ # Also the fields that are required legally may vary depending on the country
163
+ # or change over time.
164
+ if transaction.debtor_address.street_name
165
+ builder.StrtNm transaction.debtor_address.street_name
166
+ end
167
+
168
+ if transaction.debtor_address.building_number
169
+ builder.BldgNb transaction.debtor_address.building_number
170
+ end
171
+
172
+ if transaction.debtor_address.post_code
173
+ builder.PstCd transaction.debtor_address.post_code
174
+ end
175
+
176
+ if transaction.debtor_address.town_name
177
+ builder.TwnNm transaction.debtor_address.town_name
178
+ end
179
+
180
+ if transaction.debtor_address.country_code
181
+ builder.Ctry transaction.debtor_address.country_code
182
+ end
183
+
184
+ if transaction.debtor_address.address_line1
185
+ builder.AdrLine transaction.debtor_address.address_line1
186
+ end
187
+
188
+ if transaction.debtor_address.address_line2
189
+ builder.AdrLine transaction.debtor_address.address_line2
190
+ end
157
191
  end
158
192
  end
159
193
  end
@@ -38,17 +38,20 @@ module SEPA
38
38
 
39
39
  # @return [String] xml
40
40
  def to_xml(schema_name=self.known_schemas.first)
41
- raise RuntimeError.new(errors.full_messages.join("\n")) unless valid?
42
- raise RuntimeError.new("Incompatible with schema #{schema_name}!") unless schema_compatible?(schema_name)
43
-
44
- builder = Builder::XmlMarkup.new indent: 2
45
- builder.instruct!
46
- builder.Document(xml_schema(schema_name)) do
47
- builder.__send__(xml_main_tag) do
48
- build_group_header(builder)
49
- build_payment_informations(builder)
41
+ raise SEPA::Error.new(errors.full_messages.join("\n")) unless valid?
42
+ raise SEPA::Error.new("Incompatible with schema #{schema_name}!") unless schema_compatible?(schema_name)
43
+
44
+ builder = Nokogiri::XML::Builder.new do |builder|
45
+ builder.Document(xml_schema(schema_name)) do
46
+ builder.__send__(xml_main_tag) do
47
+ build_group_header(builder)
48
+ build_payment_informations(builder)
49
+ end
50
50
  end
51
51
  end
52
+
53
+ validate_final_document!(builder.doc, schema_name)
54
+ builder.to_xml
52
55
  end
53
56
 
54
57
  def amount_total(selected_transactions=transactions)
@@ -68,10 +71,10 @@ module SEPA
68
71
 
69
72
  # Set unique identifer for the message
70
73
  def message_identification=(value)
71
- raise ArgumentError.new('mesage_identification must be a string!') unless value.is_a?(String)
74
+ raise ArgumentError.new('message_identification must be a string!') unless value.is_a?(String)
72
75
 
73
76
  regex = /\A([A-Za-z0-9]|[\+|\?|\/|\-|\:|\(|\)|\.|\,|\'|\ ]){1,35}\z/
74
- raise ArgumentError.new("mesage_identification does not match #{regex}!") unless value.match(regex)
77
+ raise ArgumentError.new("message_identification does not match #{regex}!") unless value.match(regex)
75
78
 
76
79
  @message_identification = value
77
80
  end
@@ -81,6 +84,22 @@ module SEPA
81
84
  @message_identification ||= "SEPA-KING/#{SecureRandom.hex(11)}"
82
85
  end
83
86
 
87
+ # Set creation date time for the message
88
+ # p.s. Rabobank in the Netherlands only accepts the more restricted format [0-9]{4}[-][0-9]{2,2}[-][0-9]{2,2}[T][0-9]{2,2}[:][0-9]{2,2}[:][0-9]{2,2}
89
+ def creation_date_time=(value)
90
+ raise ArgumentError.new('creation_date_time must be a string!') unless value.is_a?(String)
91
+
92
+ regex = /[0-9]{4}[-][0-9]{2,2}[-][0-9]{2,2}(?:\s|T)[0-9]{2,2}[:][0-9]{2,2}[:][0-9]{2,2}/
93
+ raise ArgumentError.new("creation_date_time does not match #{regex}!") unless value.match(regex)
94
+
95
+ @creation_date_time = value
96
+ end
97
+
98
+ # Get creation date time for the message (with fallback to Time.now.iso8601)
99
+ def creation_date_time
100
+ @creation_date_time ||= Time.now.iso8601
101
+ end
102
+
84
103
  # Returns the id of the batch to which the given transaction belongs
85
104
  # Identified based upon the reference of the transaction
86
105
  def batch_id(transaction_reference)
@@ -106,7 +125,7 @@ module SEPA
106
125
  def build_group_header(builder)
107
126
  builder.GrpHdr do
108
127
  builder.MsgId(message_identification)
109
- builder.CreDtTm(Time.now.iso8601)
128
+ builder.CreDtTm(creation_date_time)
110
129
  builder.NbOfTxs(transactions.length)
111
130
  builder.CtrlSum('%.2f' % amount_total)
112
131
  builder.InitgPty do
@@ -131,5 +150,11 @@ module SEPA
131
150
  def transaction_group(transaction)
132
151
  transaction
133
152
  end
153
+
154
+ def validate_final_document!(document, schema_name)
155
+ xsd = Nokogiri::XML::Schema(File.read(File.expand_path("../../../lib/schema/#{schema_name}.xsd", __FILE__)))
156
+ errors = xsd.validate(document).map { |error| error.message }
157
+ raise SEPA::Error.new("Incompatible with schema #{schema_name}: #{errors.join(', ')}") if errors.any?
158
+ end
134
159
  end
135
160
  end
@@ -1,21 +1,24 @@
1
1
  # encoding: utf-8
2
2
  module SEPA
3
3
  class CreditTransferTransaction < Transaction
4
- attr_accessor :service_level
4
+ attr_accessor :service_level,
5
+ :creditor_address,
6
+ :category_purpose
5
7
 
6
- validates_inclusion_of :service_level, :in => %w(SEPA URGP)
8
+ validates_inclusion_of :service_level, :in => %w(SEPA URGP), :allow_nil => true
9
+ validates_length_of :category_purpose, within: 1..4, allow_nil: true
7
10
 
8
11
  validate { |t| t.validate_requested_date_after(Date.today) }
9
12
 
10
13
  def initialize(attributes = {})
11
14
  super
12
- self.service_level ||= 'SEPA'
15
+ self.service_level ||= 'SEPA' if self.currency == 'EUR'
13
16
  end
14
17
 
15
18
  def schema_compatible?(schema_name)
16
19
  case schema_name
17
20
  when PAIN_001_001_03
18
- self.service_level == 'SEPA'
21
+ !self.service_level || (self.service_level == 'SEPA' && self.currency == 'EUR')
19
22
  when PAIN_001_002_03
20
23
  self.bic.present? && self.service_level == 'SEPA' && self.currency == 'EUR'
21
24
  when PAIN_001_003_03
@@ -4,7 +4,15 @@ module SEPA
4
4
  SEQUENCE_TYPES = %w(FRST OOFF RCUR FNAL)
5
5
  LOCAL_INSTRUMENTS = %w(CORE COR1 B2B)
6
6
 
7
- attr_accessor :mandate_id, :mandate_date_of_signature, :local_instrument, :sequence_type, :creditor_account, :original_debtor_account, :same_mandate_new_debtor_agent, :original_creditor_account, :debtor_address
7
+ attr_accessor :mandate_id,
8
+ :mandate_date_of_signature,
9
+ :local_instrument,
10
+ :sequence_type,
11
+ :creditor_account,
12
+ :original_debtor_account,
13
+ :same_mandate_new_debtor_agent,
14
+ :original_creditor_account,
15
+ :debtor_address
8
16
 
9
17
  validates_with MandateIdentifierValidator, field_name: :mandate_id, message: "%{value} is invalid"
10
18
  validates_presence_of :mandate_date_of_signature
@@ -6,7 +6,18 @@ module SEPA
6
6
 
7
7
  DEFAULT_REQUESTED_DATE = Date.new(1999, 1, 1).freeze
8
8
 
9
- attr_accessor :name, :iban, :bic, :amount, :instruction, :reference, :remittance_information, :requested_date, :batch_booking, :currency, :debtor_address
9
+ attr_accessor :name,
10
+ :iban,
11
+ :bic,
12
+ :amount,
13
+ :instruction,
14
+ :reference,
15
+ :remittance_information,
16
+ :requested_date,
17
+ :batch_booking,
18
+ :currency,
19
+ :debtor_address,
20
+ :creditor_address
10
21
 
11
22
  convert :name, :instruction, :reference, :remittance_information, to: :text
12
23
  convert :amount, to: :decimal
@@ -1,3 +1,3 @@
1
1
  module SEPA
2
- VERSION = '0.11.0'
2
+ VERSION = '0.11.1'
3
3
  end
data/lib/sepa_king.rb CHANGED
@@ -1,14 +1,16 @@
1
1
  require 'active_model'
2
2
  require 'bigdecimal'
3
- require 'builder'
3
+ require 'nokogiri'
4
4
  require 'iban-tools'
5
5
 
6
+ require 'sepa_king/error'
6
7
  require 'sepa_king/converter'
7
8
  require 'sepa_king/validator'
8
9
  require 'sepa_king/account'
9
10
  require 'sepa_king/account/debtor_account'
10
11
  require 'sepa_king/account/debtor_address'
11
12
  require 'sepa_king/account/creditor_account'
13
+ require 'sepa_king/account/creditor_address'
12
14
  require 'sepa_king/transaction'
13
15
  require 'sepa_king/transaction/direct_debit_transaction'
14
16
  require 'sepa_king/transaction/credit_transfer_transaction'
data/sepa_king.gemspec CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |s|
21
21
  s.required_ruby_version = '>= 2.2'
22
22
 
23
23
  s.add_runtime_dependency 'activemodel', '>= 3.1'
24
- s.add_runtime_dependency 'builder'
24
+ s.add_runtime_dependency 'nokogiri'
25
25
  s.add_runtime_dependency 'iban-tools'
26
26
 
27
27
  s.add_development_dependency 'bundler'
@@ -29,5 +29,4 @@ Gem::Specification.new do |s|
29
29
  s.add_development_dependency 'coveralls'
30
30
  s.add_development_dependency 'simplecov'
31
31
  s.add_development_dependency 'rake'
32
- s.add_development_dependency 'nokogiri'
33
32
  end
@@ -38,7 +38,60 @@ describe SEPA::CreditTransfer do
38
38
  it 'should fail' do
39
39
  expect {
40
40
  SEPA::CreditTransfer.new.to_xml
41
- }.to raise_error(RuntimeError)
41
+ }.to raise_error(SEPA::Error)
42
+ end
43
+ end
44
+
45
+ context 'setting creditor address with adrline' do
46
+ subject do
47
+ sct = SEPA::CreditTransfer.new name: 'Schuldner GmbH',
48
+ iban: 'DE87200500001234567890'
49
+
50
+ sca = SEPA::CreditorAddress.new country_code: 'CH',
51
+ address_line1: 'Mustergasse 123',
52
+ address_line2: '1234 Musterstadt'
53
+
54
+ sct.add_transaction name: 'Telekomiker AG',
55
+ bic: 'PBNKDEFF370',
56
+ iban: 'DE37112589611964645802',
57
+ amount: 102.50,
58
+ reference: 'XYZ-1234/123',
59
+ remittance_information: 'Rechnung vom 22.08.2013',
60
+ creditor_address: sca
61
+
62
+ sct
63
+ end
64
+
65
+ it 'should validate against pain.001.003.01' do
66
+ expect(subject.to_xml(SEPA::PAIN_001_003_03)).to validate_against('pain.001.003.03.xsd')
67
+ end
68
+ end
69
+
70
+ context 'setting creditor address with structured fields' do
71
+ subject do
72
+ sct = SEPA::CreditTransfer.new name: 'Schuldner GmbH',
73
+ iban: 'DE87200500001234567890',
74
+ bic: 'BANKDEFFXXX'
75
+
76
+ sca = SEPA::CreditorAddress.new country_code: 'CH',
77
+ street_name: 'Mustergasse',
78
+ building_number: '123',
79
+ post_code: '1234',
80
+ town_name: 'Musterstadt'
81
+
82
+ sct.add_transaction name: 'Telekomiker AG',
83
+ bic: 'PBNKDEFF370',
84
+ iban: 'DE37112589611964645802',
85
+ amount: 102.50,
86
+ reference: 'XYZ-1234/123',
87
+ remittance_information: 'Rechnung vom 22.08.2013',
88
+ creditor_address: sca
89
+
90
+ sct
91
+ end
92
+
93
+ it 'should validate against pain.001.001.01' do
94
+ expect(subject.to_xml(SEPA::PAIN_001_001_03)).to validate_against('pain.001.001.03.xsd')
42
95
  end
43
96
  end
44
97
 
@@ -65,13 +118,13 @@ describe SEPA::CreditTransfer do
65
118
  it 'should fail for pain.001.001.03' do
66
119
  expect {
67
120
  subject.to_xml(SEPA::PAIN_001_001_03)
68
- }.to raise_error(RuntimeError)
121
+ }.to raise_error(SEPA::Error)
69
122
  end
70
123
 
71
124
  it 'should fail for pain.001.002.03' do
72
125
  expect {
73
126
  subject.to_xml(SEPA::PAIN_001_002_03)
74
- }.to raise_error(RuntimeError)
127
+ }.to raise_error(SEPA::Error)
75
128
  end
76
129
  end
77
130
 
@@ -248,6 +301,7 @@ describe SEPA::CreditTransfer do
248
301
  sct.add_transaction(credit_transfer_transaction.merge requested_date: Date.today + 1, batch_booking: true, amount: 2)
249
302
  sct.add_transaction(credit_transfer_transaction.merge requested_date: Date.today + 2, batch_booking: false, amount: 4)
250
303
  sct.add_transaction(credit_transfer_transaction.merge requested_date: Date.today + 2, batch_booking: true, amount: 8)
304
+ sct.add_transaction(credit_transfer_transaction.merge requested_date: Date.today + 2, batch_booking: true, category_purpose: 'SALA', amount: 6)
251
305
 
252
306
  sct.to_xml
253
307
  end
@@ -264,6 +318,9 @@ describe SEPA::CreditTransfer do
264
318
 
265
319
  expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf[4]/ReqdExctnDt', (Date.today + 2).iso8601)
266
320
  expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf[4]/BtchBookg', 'true')
321
+
322
+ expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf[5]/ReqdExctnDt', (Date.today + 2).iso8601)
323
+ expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf[5]/PmtTpInf/CtgyPurp/Cd', 'SALA')
267
324
  end
268
325
 
269
326
  it 'should have multiple control sums' do
@@ -324,13 +381,13 @@ describe SEPA::CreditTransfer do
324
381
  it 'should fail for pain.001.002.03' do
325
382
  expect {
326
383
  subject.to_xml(SEPA::PAIN_001_002_03)
327
- }.to raise_error(RuntimeError)
384
+ }.to raise_error(SEPA::Error)
328
385
  end
329
386
 
330
387
  it 'should fail for pain.001.003.03' do
331
388
  expect {
332
389
  subject.to_xml(SEPA::PAIN_001_003_03)
333
- }.to raise_error(RuntimeError)
390
+ }.to raise_error(SEPA::Error)
334
391
  end
335
392
  end
336
393
 
@@ -352,7 +409,7 @@ describe SEPA::CreditTransfer do
352
409
  it 'should fail for pain.001.002.03' do
353
410
  expect {
354
411
  subject.to_xml(SEPA::PAIN_001_002_03)
355
- }.to raise_error(RuntimeError)
412
+ }.to raise_error(SEPA::Error)
356
413
  end
357
414
 
358
415
  it 'should validate against pain.001.003.03' do
@@ -55,4 +55,14 @@ describe SEPA::CreditTransferTransaction do
55
55
  expect(SEPA::CreditTransferTransaction).not_to accept(Date.new(1995,12,21), Date.today - 1, for: :requested_date)
56
56
  end
57
57
  end
58
+
59
+ context 'Category Purpose' do
60
+ it 'should allow valid value' do
61
+ expect(SEPA::CreditTransferTransaction).to accept(nil, 'SALA', 'X' * 4, for: :category_purpose)
62
+ end
63
+
64
+ it 'should not allow invalid value' do
65
+ expect(SEPA::CreditTransferTransaction).not_to accept('', 'X' * 5, for: :category_purpose)
66
+ end
67
+ end
58
68
  end
@@ -70,19 +70,19 @@ describe SEPA::DirectDebit do
70
70
  it 'should fail' do
71
71
  expect {
72
72
  SEPA::DirectDebit.new.to_xml
73
- }.to raise_error(RuntimeError)
73
+ }.to raise_error(SEPA::Error)
74
74
  end
75
75
  end
76
76
 
77
- context 'setting debtor address' do
77
+ context 'setting debtor address with adrline' do
78
78
  subject do
79
79
  sdd = SEPA::DirectDebit.new name: 'Gläubiger GmbH',
80
80
  iban: 'DE87200500001234567890',
81
81
  creditor_identifier: 'DE98ZZZ09999999999'
82
82
 
83
- sda = SEPA::DebtorAddress.new country_code: 'CH',
84
- address_line1: 'Mustergasse 123',
85
- address_line2: '1234 Musterstadt'
83
+ sda = SEPA::DebtorAddress.new country_code: 'CH',
84
+ address_line1: 'Mustergasse 123',
85
+ address_line2: '1234 Musterstadt'
86
86
 
87
87
  sdd.add_transaction name: 'Zahlemann & Söhne GbR',
88
88
  bic: 'SPUEDE2UXXX',
@@ -102,6 +102,36 @@ describe SEPA::DirectDebit do
102
102
  end
103
103
  end
104
104
 
105
+ context 'setting debtor address with structured fields' do
106
+ subject do
107
+ sdd = SEPA::DirectDebit.new name: 'Gläubiger GmbH',
108
+ iban: 'DE87200500001234567890',
109
+ creditor_identifier: 'DE98ZZZ09999999999'
110
+
111
+ sda = SEPA::DebtorAddress.new country_code: 'CH',
112
+ street_name: 'Mustergasse',
113
+ building_number: '123',
114
+ post_code: '1234',
115
+ town_name: 'Musterstadt'
116
+
117
+ sdd.add_transaction name: 'Zahlemann & Söhne GbR',
118
+ bic: 'SPUEDE2UXXX',
119
+ iban: 'DE21500500009876543210',
120
+ amount: 39.99,
121
+ reference: 'XYZ/2013-08-ABO/12345',
122
+ remittance_information: 'Unsere Rechnung vom 10.08.2013',
123
+ mandate_id: 'K-02-2011-12345',
124
+ debtor_address: sda,
125
+ mandate_date_of_signature: Date.new(2011,1,25)
126
+
127
+ sdd
128
+ end
129
+
130
+ it 'should validate against pain.008.001.02' do
131
+ expect(subject.to_xml(SEPA::PAIN_008_001_02)).to validate_against('pain.008.001.02.xsd')
132
+ end
133
+ end
134
+
105
135
  context 'for valid creditor' do
106
136
  context 'without BIC (IBAN-only)' do
107
137
  subject do
@@ -128,7 +158,7 @@ describe SEPA::DirectDebit do
128
158
  it 'should fail for pain.008.002.02' do
129
159
  expect {
130
160
  subject.to_xml(SEPA::PAIN_008_002_02)
131
- }.to raise_error(RuntimeError)
161
+ }.to raise_error(SEPA::Error)
132
162
  end
133
163
 
134
164
  it 'should validate against pain.008.001.02' do
@@ -229,10 +259,6 @@ describe SEPA::DirectDebit do
229
259
  expect(subject).to validate_against('pain.008.003.02.xsd')
230
260
  end
231
261
 
232
- it 'should have message_identification' do
233
- expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/GrpHdr/MsgId', message_id_regex)
234
- end
235
-
236
262
  it 'should have creditor identifier' do
237
263
  expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/GrpHdr/InitgPty/Id/OrgId/Othr/Id', direct_debit.account.creditor_identifier)
238
264
  end
@@ -359,7 +385,7 @@ describe SEPA::DirectDebit do
359
385
  it 'should raise error on XML generation' do
360
386
  expect {
361
387
  subject.to_xml
362
- }.to raise_error(RuntimeError)
388
+ }.to raise_error(SEPA::Error)
363
389
  end
364
390
  end
365
391
 
@@ -498,6 +524,19 @@ describe SEPA::DirectDebit do
498
524
  end
499
525
  end
500
526
 
527
+ context 'with large message identification' do
528
+ subject do
529
+ sct = direct_debit
530
+ sct.message_identification = 'A' * 35
531
+ sct.add_transaction(direct_debt_transaction.merge(instruction: '1234/ABC'))
532
+ sct
533
+ end
534
+
535
+ it 'should fail as the payment identification becomes too large' do
536
+ expect { subject.to_xml }.to raise_error(SEPA::Error)
537
+ end
538
+ end
539
+
501
540
  context 'with a different currency given' do
502
541
  subject do
503
542
  sct = direct_debit
@@ -523,13 +562,13 @@ describe SEPA::DirectDebit do
523
562
  it 'should fail for pain.008.002.02' do
524
563
  expect {
525
564
  subject.to_xml(SEPA::PAIN_008_002_02)
526
- }.to raise_error(RuntimeError)
565
+ }.to raise_error(SEPA::Error)
527
566
  end
528
567
 
529
568
  it 'should fail for pain.008.003.02' do
530
569
  expect {
531
570
  subject.to_xml(SEPA::PAIN_008_003_02)
532
- }.to raise_error(RuntimeError)
571
+ }.to raise_error(SEPA::Error)
533
572
  end
534
573
  end
535
574
  end
data/spec/message_spec.rb CHANGED
@@ -85,4 +85,44 @@ describe SEPA::Message do
85
85
  end
86
86
  end
87
87
  end
88
+
89
+ describe :creation_date_time do
90
+ subject { DummyMessage.new }
91
+
92
+ describe 'getter' do
93
+ it 'should return Time.now.iso8601' do
94
+ expect(subject.creation_date_time).to eq(Time.now.iso8601)
95
+ end
96
+ end
97
+
98
+ describe 'setter' do
99
+ it 'should accept date time strings' do
100
+ ['2017-01-05T12:28:52', '2017-01-05T12:28:52Z', '2017-01-05 12:28:52', '2017-01-05T12:28:52+01:00'].each do |valid_dt|
101
+ subject.creation_date_time = valid_dt
102
+ expect(subject.creation_date_time).to eq(valid_dt)
103
+ end
104
+ end
105
+
106
+ it 'should deny invalid string' do
107
+ [ 'an arbitrary string',
108
+ ''
109
+ ].each do |arg|
110
+ expect {
111
+ subject.creation_date_time = arg
112
+ }.to raise_error(ArgumentError)
113
+ end
114
+ end
115
+
116
+ it 'should deny argument other than String' do
117
+ [ 123,
118
+ nil,
119
+ :foo
120
+ ].each do |arg|
121
+ expect {
122
+ subject.creation_date_time = arg
123
+ }.to raise_error(ArgumentError)
124
+ end
125
+ end
126
+ end
127
+ end
88
128
  end
@@ -27,18 +27,51 @@ describe SEPA::Transaction do
27
27
  end
28
28
 
29
29
  context 'Adress' do
30
- it 'should accept valid value' do
31
- expect(SEPA::Transaction).to accept(SEPA::DebtorAddress.new(
32
- country_code: "CH",
33
- address_line1: "Musterstrasse 123",
34
- address_line2: "1234 Musterstadt"
35
- ), for: :debtor_address)
30
+ context 'with address_line' do
31
+ it 'should accept valid value' do
32
+ expect(SEPA::Transaction).to accept(SEPA::DebtorAddress.new(
33
+ country_code: "CH",
34
+ address_line1: "Musterstrasse 123",
35
+ address_line2: "1234 Musterstadt"
36
+ ), for: :debtor_address)
37
+ end
38
+
39
+ it 'should accept valid value' do
40
+ expect(SEPA::Transaction).to accept(SEPA::CreditorAddress.new(
41
+ country_code: "CH",
42
+ address_line1: "Musterstrasse 123",
43
+ address_line2: "1234 Musterstadt"
44
+ ), for: :creditor_address)
45
+ end
46
+ end
47
+
48
+ context 'with individual address fields' do
49
+ it 'should accept valid value' do
50
+ expect(SEPA::Transaction).to accept(SEPA::DebtorAddress.new(
51
+ country_code: "CH",
52
+ street_name: 'Mustergasse',
53
+ building_number: '123',
54
+ post_code: '1234',
55
+ town_name: 'Musterstadt'
56
+ ), for: :debtor_address)
57
+ end
58
+
59
+ it 'should accept valid value' do
60
+ expect(SEPA::Transaction).to accept(SEPA::CreditorAddress.new(
61
+ country_code: "CH",
62
+ street_name: 'Mustergasse',
63
+ building_number: '123',
64
+ post_code: '1234',
65
+ town_name: 'Musterstadt'
66
+ ), for: :creditor_address)
67
+ end
36
68
  end
37
69
 
38
70
  it 'should not accept invalid value' do
39
71
  expect(SEPA::Transaction).not_to accept('', {} , for: :name)
40
72
  end
41
73
  end
74
+
42
75
  context 'IBAN' do
43
76
  it 'should accept valid value' do
44
77
  expect(SEPA::Transaction).to accept('DE21500500009876543210', 'PL61109010140000071219812874', for: :iban)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sepa_king
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.0
4
+ version: 0.11.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Georg Leciejewski
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2018-05-09 00:00:00.000000000 Z
12
+ date: 2018-10-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activemodel
@@ -26,7 +26,7 @@ dependencies:
26
26
  - !ruby/object:Gem::Version
27
27
  version: '3.1'
28
28
  - !ruby/object:Gem::Dependency
29
- name: builder
29
+ name: nokogiri
30
30
  requirement: !ruby/object:Gem::Requirement
31
31
  requirements:
32
32
  - - ">="
@@ -123,25 +123,12 @@ dependencies:
123
123
  - - ">="
124
124
  - !ruby/object:Gem::Version
125
125
  version: '0'
126
- - !ruby/object:Gem::Dependency
127
- name: nokogiri
128
- requirement: !ruby/object:Gem::Requirement
129
- requirements:
130
- - - ">="
131
- - !ruby/object:Gem::Version
132
- version: '0'
133
- type: :development
134
- prerelease: false
135
- version_requirements: !ruby/object:Gem::Requirement
136
- requirements:
137
- - - ">="
138
- - !ruby/object:Gem::Version
139
- version: '0'
140
126
  description: Implemention of pain.001.002.03 and pain.008.002.02 (ISO 20022)
141
127
  email:
142
128
  - gl@salesking.eu
143
129
  - mail@georg-ledermann.de
144
- executables: []
130
+ executables:
131
+ - rake
145
132
  extensions: []
146
133
  extra_rdoc_files: []
147
134
  files:
@@ -153,6 +140,7 @@ files:
153
140
  - LICENSE.txt
154
141
  - README.md
155
142
  - Rakefile
143
+ - bin/rake
156
144
  - gemfiles/Gemfile-activemodel-3.1.x
157
145
  - gemfiles/Gemfile-activemodel-3.2.x
158
146
  - gemfiles/Gemfile-activemodel-4.0.x
@@ -170,9 +158,11 @@ files:
170
158
  - lib/sepa_king.rb
171
159
  - lib/sepa_king/account.rb
172
160
  - lib/sepa_king/account/creditor_account.rb
161
+ - lib/sepa_king/account/creditor_address.rb
173
162
  - lib/sepa_king/account/debtor_account.rb
174
163
  - lib/sepa_king/account/debtor_address.rb
175
164
  - lib/sepa_king/converter.rb
165
+ - lib/sepa_king/error.rb
176
166
  - lib/sepa_king/message.rb
177
167
  - lib/sepa_king/message/credit_transfer.rb
178
168
  - lib/sepa_king/message/direct_debit.rb
@@ -225,7 +215,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
225
215
  version: '0'
226
216
  requirements: []
227
217
  rubyforge_project:
228
- rubygems_version: 2.7.6
218
+ rubygems_version: 2.7.7
229
219
  signing_key:
230
220
  specification_version: 4
231
221
  summary: Ruby gem for creating SEPA XML files