sepa_king 0.11.0 → 0.11.1

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.
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