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 +4 -4
- data/README.md +24 -1
- data/bin/rake +29 -0
- data/lib/sepa_king/account/creditor_address.rb +37 -0
- data/lib/sepa_king/account/debtor_address.rb +21 -7
- data/lib/sepa_king/error.rb +4 -0
- data/lib/sepa_king/message/credit_transfer.rb +49 -3
- data/lib/sepa_king/message/direct_debit.rb +37 -3
- data/lib/sepa_king/message.rb +37 -12
- data/lib/sepa_king/transaction/credit_transfer_transaction.rb +7 -4
- data/lib/sepa_king/transaction/direct_debit_transaction.rb +9 -1
- data/lib/sepa_king/transaction.rb +12 -1
- data/lib/sepa_king/version.rb +1 -1
- data/lib/sepa_king.rb +3 -1
- data/sepa_king.gemspec +1 -2
- data/spec/credit_transfer_spec.rb +63 -6
- data/spec/credit_transfer_transaction_spec.rb +10 -0
- data/spec/direct_debit_spec.rb +52 -13
- data/spec/message_spec.rb +40 -0
- data/spec/transaction_spec.rb +39 -6
- metadata +9 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 90e4af1e7d2d11456cc48e339006078182b067cd6a2c49c3314ba296fc295fc3
|
4
|
+
data.tar.gz: f224fa0fcd711759b1b56baf53c78b1bf5b8f90f1828ced8a6ce7dc21ad80e0c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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 :
|
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 :
|
10
|
-
convert :
|
11
|
-
convert :
|
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 :
|
14
|
-
validates_length_of :
|
15
|
-
validates_length_of :
|
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|
|
@@ -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
|
-
|
31
|
-
builder.
|
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
|
-
|
155
|
-
|
156
|
-
|
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
|
data/lib/sepa_king/message.rb
CHANGED
@@ -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
|
42
|
-
raise
|
43
|
-
|
44
|
-
builder = Builder
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
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('
|
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("
|
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(
|
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,
|
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,
|
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
|
data/lib/sepa_king/version.rb
CHANGED
data/lib/sepa_king.rb
CHANGED
@@ -1,14 +1,16 @@
|
|
1
1
|
require 'active_model'
|
2
2
|
require 'bigdecimal'
|
3
|
-
require '
|
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 '
|
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(
|
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(
|
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(
|
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(
|
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(
|
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(
|
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
|
data/spec/direct_debit_spec.rb
CHANGED
@@ -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(
|
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:
|
84
|
-
|
85
|
-
|
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(
|
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(
|
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(
|
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(
|
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
|
data/spec/transaction_spec.rb
CHANGED
@@ -27,18 +27,51 @@ describe SEPA::Transaction do
|
|
27
27
|
end
|
28
28
|
|
29
29
|
context 'Adress' do
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
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.
|
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-
|
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:
|
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.
|
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
|