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