sepa_king 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5cc20b05ec4228c5ed453503dd90329e3b6366b3
4
- data.tar.gz: 884d0f00387ef8d8a50b06a529a92d4220897773
3
+ metadata.gz: 2314875acf744a2dad9a545b56d75a27672e29e9
4
+ data.tar.gz: dc3f98095e43274d6eeafab00a46f68abc9f8728
5
5
  SHA512:
6
- metadata.gz: 21b8f10e50d79e84d5a9ef67a54b15999e48efa0d13e8bc9df75387638395cc852e37c2e7f8541763aaee9fd74eee3e969d95a08a04a744e3ad16869ac326eaf
7
- data.tar.gz: ae7a218d84ba1ee072079a1720d9da747d8c6d6c24179db3ec4b7cc7d1b7e3ac61556a9260836d36eeabf33111efd97910a210709f7d75ca6a17b12a45e28dc9
6
+ metadata.gz: 2f1f366521a4884bea53d0e0b9989039af4d94e54936df4889460f4aa3926dc9520e82d4018dcdd88e88c8b76abedf4963880f01320971d818561d89b06206b6
7
+ data.tar.gz: 720f897af281d19848ef4396cda5b2e1faf9aa17b823226972f9f3d2e5276aeb7bbde2c71c674bd679aeaa36422d9174cbb6177b0d5f45b673e989805c2bbd4a
data/README.md CHANGED
@@ -3,15 +3,25 @@
3
3
  [![Build Status](https://secure.travis-ci.org/salesking/sepa_king.png)](http://travis-ci.org/salesking/sepa_king)
4
4
  [![Code Climate](https://codeclimate.com/github/salesking/sepa_king.png)](https://codeclimate.com/github/salesking/sepa_king)
5
5
  [![Coverage Status](https://coveralls.io/repos/salesking/sepa_king/badge.png)](https://coveralls.io/r/salesking/sepa_king)
6
+ [![Gem Version](https://badge.fury.io/rb/sepa_king.png)](http://badge.fury.io/rb/sepa_king)
7
+ [![Dependency Status](https://gemnasium.com/salesking/sepa_king.png)](https://gemnasium.com/salesking/sepa_king)
6
8
 
7
9
  We love building payment applications! So after developing the [DTAUS library for Ruby](https://github.com/salesking/king_dtaus) we move on with SEPA.
8
10
 
9
11
 
10
12
  ## Features
11
13
 
12
- * Credit transfer initiation (pain.001.002.03)
13
- * Debit transfer initiation (pain.008.002.02)
14
- * Tested with Ruby 1.9.3 and 2.0.0
14
+ This gem implements the following two messages out of the ISO 20022 standard:
15
+
16
+ * Credit Transfer Initiation (pain.001.002.03)
17
+ * Direct Debit Initiation (pain.008.002.02)
18
+
19
+ BTW: **pain** is a shortcut for **Pa**yment **In**itiation.
20
+
21
+
22
+ ## Requirements
23
+
24
+ * Ruby 1.9.3 or 2.0.0
15
25
 
16
26
 
17
27
  ## Installation
@@ -45,15 +55,15 @@ sdd = SEPA::DirectDebit.new(
45
55
 
46
56
  # Second: Add transactions
47
57
  sdd.add_transaction(
48
- # Name of the debitor, in German: "Zahlungspflichtiger"
58
+ # Name of the debtor, in German: "Zahlungspflichtiger"
49
59
  # String, max. 70 char
50
60
  name: 'Zahlemann & Söhne GbR',
51
61
 
52
- # Business Identifier Code (SWIFT-Code) of the debitor's account
62
+ # Business Identifier Code (SWIFT-Code) of the debtor's account
53
63
  # String, 8 or 11 char
54
64
  bic: 'SPUEDE2UXXX',
55
65
 
56
- # International Bank Account Number of the debitor's account
66
+ # International Bank Account Number of the debtor's account
57
67
  # String, max. 34 chars
58
68
  iban: 'DE21500500009876543210',
59
69
 
@@ -61,11 +71,11 @@ sdd.add_transaction(
61
71
  # Number with two decimal digit
62
72
  amount: 39.99,
63
73
 
64
- # OPTIONAL: End-To-End-Identification, will be submitted to the debitor
74
+ # OPTIONAL: End-To-End-Identification, will be submitted to the debtor
65
75
  # String, max. 35 char
66
76
  reference: 'XYZ/2013-08-ABO/6789',
67
77
 
68
- # OPTIONAL: Unstructured remittance Information, in German "Verwendungszweck"
78
+ # OPTIONAL: Unstructured remittance information, in German "Verwendungszweck"
69
79
  # String, max. 140 char
70
80
  remittance_information: 'Vielen Dank für Ihren Einkauf!',
71
81
 
@@ -78,13 +88,13 @@ sdd.add_transaction(
78
88
  mandate_date_of_signature: Date.new(2011,1,25),
79
89
 
80
90
  # Local instrument, in German "Lastschriftart"
81
- # One of this strings:
91
+ # One of these strings:
82
92
  # 'CORE' ("Basis-Lastschrift")
83
93
  # 'B2B' ("Firmen-Lastschrift")
84
94
  local_instrument: 'CORE',
85
95
 
86
96
  # Sequence type
87
- # One of this strings:
97
+ # One of these strings:
88
98
  # 'FRST' ("Erst-Lastschrift")
89
99
  # 'RCUR' ("Folge-Lastschrift")
90
100
  # 'OOFF' ("Einmalige Lastschrift")
@@ -96,6 +106,7 @@ sdd.add_transaction(
96
106
  requested_date: Date.new(2013,9,5),
97
107
 
98
108
  # OPTIONAL: Enables or disables batch booking, in German "Sammelbuchung / Einzelbuchung"
109
+ # True or False
99
110
  batch_booking: true
100
111
  )
101
112
  sdd.add_transaction ...
@@ -110,15 +121,15 @@ How to create the XML for **Credit Transfer Initiation** (in german: "Überweisu
110
121
  ```ruby
111
122
  # First: Create the main object
112
123
  sct = SEPA::CreditTransfer.new(
113
- # Name of the initiating party and debitor, in German: "Auftraggeber"
124
+ # Name of the initiating party and debtor, in German: "Auftraggeber"
114
125
  # String, max. 70 char
115
126
  name: 'Schuldner GmbH',
116
127
 
117
- # Business Identifier Code (SWIFT-Code) of the debitor
128
+ # Business Identifier Code (SWIFT-Code) of the debtor
118
129
  # String, 8 or 11 char
119
130
  bic: 'BANKDEFFXXX',
120
131
 
121
- # International Bank Account Number of the debitor
132
+ # International Bank Account Number of the debtor
122
133
  # String, max. 34 chars
123
134
  iban: 'DE87200500001234567890'
124
135
  )
@@ -145,9 +156,17 @@ sct.add_transaction(
145
156
  # String, max. 35 char
146
157
  reference: 'XYZ-1234/123',
147
158
 
148
- # OPTIONAL: Unstructured remittance Information, in German "Verwendungszweck"
159
+ # OPTIONAL: Unstructured remittance information, in German "Verwendungszweck"
149
160
  # String, max. 140 char
150
161
  remittance_information: 'Rechnung vom 22.08.2013'
162
+
163
+ # OPTIONAL: Requested execution date, in German "Ausführungstermin"
164
+ # Date
165
+ requested_date: Date.new(2013,9,5),
166
+
167
+ # OPTIONAL: Enables or disables batch booking, in German "Sammelbuchung / Einzelbuchung"
168
+ # True or False
169
+ batch_booking: true
151
170
  )
152
171
  sct.add_transaction ...
153
172
 
@@ -2,7 +2,11 @@
2
2
 
3
3
  module SEPA
4
4
  class Message
5
+ include ActiveModel::Validations
6
+
5
7
  attr_reader :account, :transactions
8
+ validates_presence_of :transactions
9
+
6
10
  class_attribute :account_class, :transaction_class, :xml_main_tag
7
11
 
8
12
  def initialize(account_options={})
@@ -19,6 +23,7 @@ module SEPA
19
23
  # @return [String] xml
20
24
  def to_xml
21
25
  raise RuntimeError.new(account.errors.full_messages.join("\n")) unless account.valid?
26
+ raise RuntimeError.new(errors.full_messages.join("\n")) unless valid?
22
27
 
23
28
  builder = Builder::XmlMarkup.new indent: 2
24
29
  builder.instruct!
@@ -47,12 +52,16 @@ module SEPA
47
52
  end
48
53
  end
49
54
 
55
+ # Unique identifer for the whole message
50
56
  def message_identification
51
- "SEPA-KING/#{Time.now.iso8601}"
57
+ @message_identification ||= "SEPA-KING/#{Time.now.to_i}"
52
58
  end
53
59
 
60
+ # Unique and consecutive identifier (used for the <PmntInf> blocks)
54
61
  def payment_information_identification
55
- message_identification
62
+ @payment_information_counter ||= 0
63
+ @payment_information_counter += 1
64
+ "#{message_identification}/#{@payment_information_counter}"
56
65
  end
57
66
  end
58
67
  end
@@ -6,6 +6,12 @@ module SEPA
6
6
  self.transaction_class = DirectDebitTransaction
7
7
  self.xml_main_tag = 'CstmrDrctDbtInitn'
8
8
 
9
+ validate do |record|
10
+ if record.transactions.map(&:local_instrument).uniq.size > 1
11
+ errors.add(:base, 'CORE and B2B must not be mixed in one message!')
12
+ end
13
+ end
14
+
9
15
  private
10
16
  # @return {Hash<Symbol=>String>} xml schema information used in output xml
11
17
  def xml_schema
@@ -1,3 +1,3 @@
1
1
  module SEPA
2
- VERSION = '0.0.5'
2
+ VERSION = '0.0.6'
3
3
  end
@@ -67,6 +67,18 @@ describe SEPA::CreditTransfer do
67
67
  expect(subject).to validate_against('pain.001.002.03.xsd')
68
68
  end
69
69
 
70
+ it 'should have message_identification' do
71
+ subject.should have_xml('//Document/CstmrCdtTrfInitn/GrpHdr/MsgId', /SEPA-KING\/[0-9]+/)
72
+ end
73
+
74
+ it 'should contain <PmtInfId>' do
75
+ subject.should have_xml('//Document/CstmrCdtTrfInitn/PmtInf/PmtInfId', /SEPA-KING\/[0-9]+\/1/)
76
+ end
77
+
78
+ it 'should contain <ReqdExctnDt>' do
79
+ subject.should have_xml('//Document/CstmrCdtTrfInitn/PmtInf/ReqdExctnDt', Date.today.next.iso8601)
80
+ end
81
+
70
82
  it 'should contain <PmtMtd>' do
71
83
  subject.should have_xml('//Document/CstmrCdtTrfInitn/PmtInf/PmtMtd', 'TRF')
72
84
  end
@@ -143,6 +155,11 @@ describe SEPA::CreditTransfer do
143
155
 
144
156
  subject.should_not have_xml('//Document/CstmrCdtTrfInitn/PmtInf[3]')
145
157
  end
158
+
159
+ it 'should contain two payment_informations with different <PmtInfId>' do
160
+ subject.should have_xml('//Document/CstmrCdtTrfInitn/PmtInf[1]/PmtInfId', /SEPA-KING\/[0-9]+\/1/)
161
+ subject.should have_xml('//Document/CstmrCdtTrfInitn/PmtInf[2]/PmtInfId', /SEPA-KING\/[0-9]+\/2/)
162
+ end
146
163
  end
147
164
 
148
165
  context 'with different batch_booking given' do
@@ -72,6 +72,14 @@ describe SEPA::DirectDebit do
72
72
  expect(subject).to validate_against('pain.008.002.02.xsd')
73
73
  end
74
74
 
75
+ it 'should have message_identification' do
76
+ subject.should have_xml('//Document/CstmrDrctDbtInitn/GrpHdr/MsgId', /SEPA-KING\/[0-9]+/)
77
+ end
78
+
79
+ it 'should contain <PmtInfId>' do
80
+ subject.should have_xml('//Document/CstmrDrctDbtInitn/PmtInf/PmtInfId', /SEPA-KING\/[0-9]+\/1/)
81
+ end
82
+
75
83
  it 'should contain <ReqdColltnDt>' do
76
84
  subject.should have_xml('//Document/CstmrDrctDbtInitn/PmtInf/ReqdColltnDt', Date.today.next.iso8601)
77
85
  end
@@ -166,6 +174,11 @@ describe SEPA::DirectDebit do
166
174
 
167
175
  subject.should_not have_xml('//Document/CstmrDrctDbtInitn/PmtInf[3]')
168
176
  end
177
+
178
+ it 'should contain two payment_informations with different <PmtInfId>' do
179
+ subject.should have_xml('//Document/CstmrDrctDbtInitn/PmtInf[1]/PmtInfId', /SEPA-KING\/[0-9]+\/1/)
180
+ subject.should have_xml('//Document/CstmrDrctDbtInitn/PmtInf[2]/PmtInfId', /SEPA-KING\/[0-9]+\/2/)
181
+ end
169
182
  end
170
183
 
171
184
  context 'with different local_instrument given' do
@@ -174,16 +187,18 @@ describe SEPA::DirectDebit do
174
187
 
175
188
  sdd.add_transaction(direct_debt_transaction.merge local_instrument: 'CORE')
176
189
  sdd.add_transaction(direct_debt_transaction.merge local_instrument: 'B2B')
177
- sdd.add_transaction(direct_debt_transaction.merge local_instrument: 'B2B')
178
190
 
179
- sdd.to_xml
191
+ sdd
180
192
  end
181
193
 
182
- it 'should contain two payment_informations with <LclInstrm>' do
183
- subject.should have_xml('//Document/CstmrDrctDbtInitn/PmtInf[1]/PmtTpInf/LclInstrm/Cd', 'CORE')
184
- subject.should have_xml('//Document/CstmrDrctDbtInitn/PmtInf[2]/PmtTpInf/LclInstrm/Cd', 'B2B')
194
+ it 'should have errors' do
195
+ subject.should have(1).error_on(:base)
196
+ end
185
197
 
186
- subject.should_not have_xml('//Document/CstmrDrctDbtInitn/PmtInf[3]')
198
+ it 'should raise error on XML generation' do
199
+ expect {
200
+ subject.to_xml
201
+ }.to raise_error(RuntimeError)
187
202
  end
188
203
  end
189
204
 
@@ -229,50 +244,26 @@ describe SEPA::DirectDebit do
229
244
  subject do
230
245
  sdd = direct_debit
231
246
 
232
- sdd.add_transaction(direct_debt_transaction.merge requested_date: Date.today + 1, local_instrument: 'CORE', sequence_type: 'OOFF', amount: 1)
233
- sdd.add_transaction(direct_debt_transaction.merge requested_date: Date.today + 1, local_instrument: 'CORE', sequence_type: 'FNAL', amount: 2)
234
- sdd.add_transaction(direct_debt_transaction.merge requested_date: Date.today + 1, local_instrument: 'B2B', sequence_type: 'OOFF', amount: 4)
235
- sdd.add_transaction(direct_debt_transaction.merge requested_date: Date.today + 1, local_instrument: 'B2B', sequence_type: 'FNAL', amount: 8)
236
- sdd.add_transaction(direct_debt_transaction.merge requested_date: Date.today + 2, local_instrument: 'CORE', sequence_type: 'OOFF', amount: 16)
237
- sdd.add_transaction(direct_debt_transaction.merge requested_date: Date.today + 2, local_instrument: 'CORE', sequence_type: 'FNAL', amount: 32)
238
- sdd.add_transaction(direct_debt_transaction.merge requested_date: Date.today + 2, local_instrument: 'B2B', sequence_type: 'OOFF', amount: 64)
239
- sdd.add_transaction(direct_debt_transaction.merge requested_date: Date.today + 2, local_instrument: 'B2B', sequence_type: 'FNAL', amount: 128)
247
+ sdd.add_transaction(direct_debt_transaction.merge requested_date: Date.today + 1, sequence_type: 'OOFF', amount: 1)
248
+ sdd.add_transaction(direct_debt_transaction.merge requested_date: Date.today + 1, sequence_type: 'FNAL', amount: 2)
249
+ sdd.add_transaction(direct_debt_transaction.merge requested_date: Date.today + 2, sequence_type: 'OOFF', amount: 4)
250
+ sdd.add_transaction(direct_debt_transaction.merge requested_date: Date.today + 2, sequence_type: 'FNAL', amount: 8)
240
251
 
241
252
  sdd.to_xml
242
253
  end
243
254
 
244
255
  it 'should contain multiple payment_informations' do
245
256
  subject.should have_xml('//Document/CstmrDrctDbtInitn/PmtInf[1]/ReqdColltnDt', (Date.today + 1).iso8601)
246
- subject.should have_xml('//Document/CstmrDrctDbtInitn/PmtInf[1]/PmtTpInf/LclInstrm/Cd', 'CORE')
247
257
  subject.should have_xml('//Document/CstmrDrctDbtInitn/PmtInf[1]/PmtTpInf/SeqTp', 'OOFF')
248
258
 
249
259
  subject.should have_xml('//Document/CstmrDrctDbtInitn/PmtInf[2]/ReqdColltnDt', (Date.today + 1).iso8601)
250
- subject.should have_xml('//Document/CstmrDrctDbtInitn/PmtInf[2]/PmtTpInf/LclInstrm/Cd', 'CORE')
251
260
  subject.should have_xml('//Document/CstmrDrctDbtInitn/PmtInf[2]/PmtTpInf/SeqTp', 'FNAL')
252
261
 
253
- subject.should have_xml('//Document/CstmrDrctDbtInitn/PmtInf[3]/ReqdColltnDt', (Date.today + 1).iso8601)
254
- subject.should have_xml('//Document/CstmrDrctDbtInitn/PmtInf[3]/PmtTpInf/LclInstrm/Cd', 'B2B')
262
+ subject.should have_xml('//Document/CstmrDrctDbtInitn/PmtInf[3]/ReqdColltnDt', (Date.today + 2).iso8601)
255
263
  subject.should have_xml('//Document/CstmrDrctDbtInitn/PmtInf[3]/PmtTpInf/SeqTp', 'OOFF')
256
264
 
257
- subject.should have_xml('//Document/CstmrDrctDbtInitn/PmtInf[4]/ReqdColltnDt', (Date.today + 1).iso8601)
258
- subject.should have_xml('//Document/CstmrDrctDbtInitn/PmtInf[4]/PmtTpInf/LclInstrm/Cd', 'B2B')
265
+ subject.should have_xml('//Document/CstmrDrctDbtInitn/PmtInf[4]/ReqdColltnDt', (Date.today + 2).iso8601)
259
266
  subject.should have_xml('//Document/CstmrDrctDbtInitn/PmtInf[4]/PmtTpInf/SeqTp', 'FNAL')
260
-
261
- subject.should have_xml('//Document/CstmrDrctDbtInitn/PmtInf[5]/ReqdColltnDt', (Date.today + 2).iso8601)
262
- subject.should have_xml('//Document/CstmrDrctDbtInitn/PmtInf[5]/PmtTpInf/LclInstrm/Cd', 'CORE')
263
- subject.should have_xml('//Document/CstmrDrctDbtInitn/PmtInf[5]/PmtTpInf/SeqTp', 'OOFF')
264
-
265
- subject.should have_xml('//Document/CstmrDrctDbtInitn/PmtInf[6]/ReqdColltnDt', (Date.today + 2).iso8601)
266
- subject.should have_xml('//Document/CstmrDrctDbtInitn/PmtInf[6]/PmtTpInf/LclInstrm/Cd', 'CORE')
267
- subject.should have_xml('//Document/CstmrDrctDbtInitn/PmtInf[6]/PmtTpInf/SeqTp', 'FNAL')
268
-
269
- subject.should have_xml('//Document/CstmrDrctDbtInitn/PmtInf[7]/ReqdColltnDt', (Date.today + 2).iso8601)
270
- subject.should have_xml('//Document/CstmrDrctDbtInitn/PmtInf[7]/PmtTpInf/LclInstrm/Cd', 'B2B')
271
- subject.should have_xml('//Document/CstmrDrctDbtInitn/PmtInf[7]/PmtTpInf/SeqTp', 'OOFF')
272
-
273
- subject.should have_xml('//Document/CstmrDrctDbtInitn/PmtInf[8]/ReqdColltnDt', (Date.today + 2).iso8601)
274
- subject.should have_xml('//Document/CstmrDrctDbtInitn/PmtInf[8]/PmtTpInf/LclInstrm/Cd', 'B2B')
275
- subject.should have_xml('//Document/CstmrDrctDbtInitn/PmtInf[8]/PmtTpInf/SeqTp', 'FNAL')
276
267
  end
277
268
 
278
269
  it 'should have multiple control sums' do
@@ -280,10 +271,6 @@ describe SEPA::DirectDebit do
280
271
  subject.should have_xml('//Document/CstmrDrctDbtInitn/PmtInf[2]/CtrlSum', '2.00')
281
272
  subject.should have_xml('//Document/CstmrDrctDbtInitn/PmtInf[3]/CtrlSum', '4.00')
282
273
  subject.should have_xml('//Document/CstmrDrctDbtInitn/PmtInf[4]/CtrlSum', '8.00')
283
- subject.should have_xml('//Document/CstmrDrctDbtInitn/PmtInf[5]/CtrlSum', '16.00')
284
- subject.should have_xml('//Document/CstmrDrctDbtInitn/PmtInf[6]/CtrlSum', '32.00')
285
- subject.should have_xml('//Document/CstmrDrctDbtInitn/PmtInf[7]/CtrlSum', '64.00')
286
- subject.should have_xml('//Document/CstmrDrctDbtInitn/PmtInf[8]/CtrlSum', '128.00')
287
274
  end
288
275
  end
289
276
  end
data/spec/message_spec.rb CHANGED
@@ -26,4 +26,12 @@ describe SEPA::Message do
26
26
  message.amount_total([message.transactions[0]]).should == 1.1
27
27
  end
28
28
  end
29
+
30
+ describe 'validation' do
31
+ it 'should fail without transactions' do
32
+ message = DummyMessage.new
33
+ message.should_not be_valid
34
+ message.should have(1).error_on(:transactions)
35
+ end
36
+ end
29
37
  end
@@ -24,7 +24,11 @@ RSpec::Matchers.define :have_xml do |xpath, text|
24
24
  nodes.should_not be_empty
25
25
  if text
26
26
  nodes.each do |node|
27
- node.content.should == text
27
+ if text.is_a?(Regexp)
28
+ node.content.should =~ text
29
+ else
30
+ node.content.should == text
31
+ end
28
32
  end
29
33
  end
30
34
  true
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.0.5
4
+ version: 0.0.6
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: 2013-08-29 00:00:00.000000000 Z
12
+ date: 2013-09-04 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activemodel