sepa_king 0.0.5 → 0.0.6

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