sepa_king_codeur 0.12.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +37 -0
  5. data/CONTRIBUTING.md +38 -0
  6. data/Gemfile +2 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +297 -0
  9. data/Rakefile +6 -0
  10. data/gemfiles/Gemfile-activemodel-3.1.x +5 -0
  11. data/gemfiles/Gemfile-activemodel-3.2.x +5 -0
  12. data/gemfiles/Gemfile-activemodel-4.0.x +5 -0
  13. data/gemfiles/Gemfile-activemodel-4.1.x +5 -0
  14. data/gemfiles/Gemfile-activemodel-4.2.x +5 -0
  15. data/gemfiles/Gemfile-activemodel-5.0.x +5 -0
  16. data/gemfiles/Gemfile-activemodel-5.1.x +5 -0
  17. data/gemfiles/Gemfile-activemodel-5.2.x +5 -0
  18. data/gemfiles/Gemfile-activemodel-6.0.x +5 -0
  19. data/lib/schema/pain.001.001.03.ch.02.xsd +1212 -0
  20. data/lib/schema/pain.001.001.03.xsd +921 -0
  21. data/lib/schema/pain.001.002.03.xsd +450 -0
  22. data/lib/schema/pain.001.003.03.xsd +474 -0
  23. data/lib/schema/pain.008.001.02.xsd +879 -0
  24. data/lib/schema/pain.008.002.02.xsd +597 -0
  25. data/lib/schema/pain.008.003.02.xsd +614 -0
  26. data/lib/sepa_king.rb +19 -0
  27. data/lib/sepa_king/account.rb +19 -0
  28. data/lib/sepa_king/account/creditor_account.rb +8 -0
  29. data/lib/sepa_king/account/creditor_address.rb +37 -0
  30. data/lib/sepa_king/account/debtor_account.rb +5 -0
  31. data/lib/sepa_king/account/debtor_address.rb +37 -0
  32. data/lib/sepa_king/converter.rb +51 -0
  33. data/lib/sepa_king/error.rb +4 -0
  34. data/lib/sepa_king/message.rb +169 -0
  35. data/lib/sepa_king/message/credit_transfer.rb +137 -0
  36. data/lib/sepa_king/message/direct_debit.rb +207 -0
  37. data/lib/sepa_king/transaction.rb +56 -0
  38. data/lib/sepa_king/transaction/credit_transfer_transaction.rb +31 -0
  39. data/lib/sepa_king/transaction/direct_debit_transaction.rb +56 -0
  40. data/lib/sepa_king/validator.rb +57 -0
  41. data/lib/sepa_king/version.rb +3 -0
  42. data/sepa_king.gemspec +33 -0
  43. data/spec/account_spec.rb +42 -0
  44. data/spec/converter_spec.rb +74 -0
  45. data/spec/credit_transfer_spec.rb +520 -0
  46. data/spec/credit_transfer_transaction_spec.rb +74 -0
  47. data/spec/creditor_account_spec.rb +23 -0
  48. data/spec/debtor_account_spec.rb +12 -0
  49. data/spec/debtor_address_spec.rb +12 -0
  50. data/spec/direct_debit_spec.rb +657 -0
  51. data/spec/direct_debit_transaction_spec.rb +69 -0
  52. data/spec/examples/pain.001.001.03.ch.02.xml +172 -0
  53. data/spec/examples/pain.001.001.03.xml +89 -0
  54. data/spec/examples/pain.001.002.03.xml +89 -0
  55. data/spec/examples/pain.001.003.03.xml +89 -0
  56. data/spec/examples/pain.008.002.02.xml +134 -0
  57. data/spec/examples/pain.008.003.02.xml +134 -0
  58. data/spec/message_spec.rb +128 -0
  59. data/spec/spec_helper.rb +33 -0
  60. data/spec/support/active_model.rb +30 -0
  61. data/spec/support/custom_matcher.rb +60 -0
  62. data/spec/support/factories.rb +24 -0
  63. data/spec/support/validations.rb +27 -0
  64. data/spec/transaction_spec.rb +134 -0
  65. data/spec/validation_spec.rb +25 -0
  66. data/spec/validator_spec.rb +99 -0
  67. metadata +250 -0
@@ -0,0 +1,74 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe SEPA::CreditTransferTransaction do
5
+ describe :initialize do
6
+ it 'should initialize a valid transaction' do
7
+ expect(
8
+ SEPA::CreditTransferTransaction.new name: 'Telekomiker AG',
9
+ iban: 'DE37112589611964645802',
10
+ bic: 'PBNKDEFF370',
11
+ amount: 102.50,
12
+ reference: 'XYZ-1234/123',
13
+ remittance_information: 'Rechnung 123 vom 22.08.2013'
14
+ ).to be_valid
15
+ end
16
+ end
17
+
18
+ describe :schema_compatible? do
19
+ context 'for pain.001.003.03' do
20
+ it 'should succeed' do
21
+ expect(SEPA::CreditTransferTransaction.new({})).to be_schema_compatible('pain.001.003.03')
22
+ end
23
+
24
+ it 'should fail for invalid attributes' do
25
+ expect(SEPA::CreditTransferTransaction.new(:currency => 'CHF')).not_to be_schema_compatible('pain.001.003.03')
26
+ end
27
+ end
28
+
29
+ context 'pain.001.002.03' do
30
+ it 'should succeed for valid attributes' do
31
+ expect(SEPA::CreditTransferTransaction.new(:bic => 'SPUEDE2UXXX', :service_level => 'SEPA')).to be_schema_compatible('pain.001.002.03')
32
+ end
33
+
34
+ it 'should fail for invalid attributes' do
35
+ expect(SEPA::CreditTransferTransaction.new(:bic => nil)).not_to be_schema_compatible('pain.001.002.03')
36
+ expect(SEPA::CreditTransferTransaction.new(:bic => 'SPUEDE2UXXX', :service_level => 'URGP')).not_to be_schema_compatible('pain.001.002.03')
37
+ expect(SEPA::CreditTransferTransaction.new(:bic => 'SPUEDE2UXXX', :currency => 'CHF')).not_to be_schema_compatible('pain.001.002.03')
38
+ end
39
+ end
40
+
41
+ context 'for pain.001.001.03' do
42
+ it 'should succeed for valid attributes' do
43
+ expect(SEPA::CreditTransferTransaction.new(:bic => 'SPUEDE2UXXX', :currency => 'CHF')).to be_schema_compatible('pain.001.001.03')
44
+ expect(SEPA::CreditTransferTransaction.new(:bic => nil)).to be_schema_compatible('pain.001.003.03')
45
+ end
46
+ end
47
+
48
+ context 'for pain.001.001.03.ch.02' do
49
+ it 'should succeed for valid attributes' do
50
+ expect(SEPA::CreditTransferTransaction.new(:bic => 'SPUEDE2UXXX', :currency => 'CHF')).to be_schema_compatible('pain.001.001.03.ch.02')
51
+ end
52
+ end
53
+ end
54
+
55
+ context 'Requested date' do
56
+ it 'should allow valid value' do
57
+ expect(SEPA::CreditTransferTransaction).to accept(nil, Date.new(1999, 1, 1), Date.today, Date.today.next, Date.today + 2, for: :requested_date)
58
+ end
59
+
60
+ it 'should not allow invalid value' do
61
+ expect(SEPA::CreditTransferTransaction).not_to accept(Date.new(1995,12,21), Date.today - 1, for: :requested_date)
62
+ end
63
+ end
64
+
65
+ context 'Category Purpose' do
66
+ it 'should allow valid value' do
67
+ expect(SEPA::CreditTransferTransaction).to accept(nil, 'SALA', 'X' * 4, for: :category_purpose)
68
+ end
69
+
70
+ it 'should not allow invalid value' do
71
+ expect(SEPA::CreditTransferTransaction).not_to accept('', 'X' * 5, for: :category_purpose)
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,23 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe SEPA::CreditorAccount do
5
+ it 'should initialize a new account' do
6
+ expect(
7
+ SEPA::CreditorAccount.new name: 'Gläubiger GmbH',
8
+ bic: 'BANKDEFFXXX',
9
+ iban: 'DE87200500001234567890',
10
+ creditor_identifier: 'DE98ZZZ09999999999'
11
+ ).to be_valid
12
+ end
13
+
14
+ describe :creditor_identifier do
15
+ it 'should accept valid value' do
16
+ expect(SEPA::CreditorAccount).to accept('DE98ZZZ09999999999', 'AT12ZZZ00000000001', 'IT97ZZZA1B2C3D4E5F6G7H8', 'NL97ZZZ123456780001', 'FR12ZZZ123456', for: :creditor_identifier)
17
+ end
18
+
19
+ it 'should not accept invalid value' do
20
+ expect(SEPA::CreditorAccount).not_to accept('', 'invalid', 'DE98ZZZ099999999990', 'DEAAAAAAAAAAAAAAAA', for: :creditor_identifier)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,12 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe SEPA::DebtorAccount do
5
+ it 'should initialize a new account' do
6
+ expect(
7
+ SEPA::DebtorAccount.new name: 'Gläubiger GmbH',
8
+ bic: 'BANKDEFFXXX',
9
+ iban: 'DE87200500001234567890'
10
+ ).to be_valid
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe SEPA::DebtorAddress do
5
+ it 'should initialize a new address' do
6
+ expect(
7
+ SEPA::DebtorAddress.new country_code: 'CH',
8
+ address_line1: 'Mustergasse 123',
9
+ address_line2: '12345 Musterstadt'
10
+ ).to be_valid
11
+ end
12
+ end
@@ -0,0 +1,657 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe SEPA::DirectDebit do
5
+ let(:message_id_regex) { /SEPA-KING\/[0-9a-z_]{22}/ }
6
+
7
+ let(:direct_debit) {
8
+ SEPA::DirectDebit.new name: 'Gläubiger GmbH',
9
+ bic: 'BANKDEFFXXX',
10
+ iban: 'DE87200500001234567890',
11
+ creditor_identifier: 'DE98ZZZ09999999999'
12
+ }
13
+
14
+ describe :new do
15
+ it 'should accept missing options' do
16
+ expect {
17
+ SEPA::DirectDebit.new
18
+ }.to_not raise_error
19
+ end
20
+ end
21
+
22
+ describe :add_transaction do
23
+ it 'should add valid transactions' do
24
+ 3.times do
25
+ direct_debit.add_transaction(direct_debt_transaction)
26
+ end
27
+
28
+ expect(direct_debit.transactions.size).to eq(3)
29
+ end
30
+
31
+ it 'should fail for invalid transaction' do
32
+ expect {
33
+ direct_debit.add_transaction name: ''
34
+ }.to raise_error(ArgumentError)
35
+ end
36
+ end
37
+
38
+ describe :batch_id do
39
+ it 'returns the id of the batch where the given transactions belongs to (1 batch)' do
40
+ direct_debit.add_transaction(direct_debt_transaction(reference: "EXAMPLE REFERENCE"))
41
+
42
+ expect(direct_debit.batch_id("EXAMPLE REFERENCE")).to match(/#{message_id_regex}\/1/)
43
+ end
44
+
45
+ it 'returns the id of the batch where the given transactions belongs to (2 batches)' do
46
+ direct_debit.add_transaction(direct_debt_transaction(reference: "EXAMPLE REFERENCE 1"))
47
+ direct_debit.add_transaction(direct_debt_transaction(reference: "EXAMPLE REFERENCE 2", requested_date: Date.today.next.next))
48
+ direct_debit.add_transaction(direct_debt_transaction(reference: "EXAMPLE REFERENCE 3"))
49
+
50
+ expect(direct_debit.batch_id("EXAMPLE REFERENCE 1")).to match(/#{message_id_regex}\/1/)
51
+ expect(direct_debit.batch_id("EXAMPLE REFERENCE 2")).to match(/#{message_id_regex}\/2/)
52
+ expect(direct_debit.batch_id("EXAMPLE REFERENCE 3")).to match(/#{message_id_regex}\/1/)
53
+ end
54
+ end
55
+
56
+ describe :batches do
57
+ it 'returns an array of batch ids in the sepa message' do
58
+ direct_debit.add_transaction(direct_debt_transaction(reference: "EXAMPLE REFERENCE 1"))
59
+ direct_debit.add_transaction(direct_debt_transaction(reference: "EXAMPLE REFERENCE 2", requested_date: Date.today.next.next))
60
+ direct_debit.add_transaction(direct_debt_transaction(reference: "EXAMPLE REFERENCE 3"))
61
+
62
+ expect(direct_debit.batches.size).to eq(2)
63
+ expect(direct_debit.batches[0]).to match(/#{message_id_regex}\/[0-9]+/)
64
+ expect(direct_debit.batches[1]).to match(/#{message_id_regex}\/[0-9]+/)
65
+ end
66
+ end
67
+
68
+ describe :to_xml do
69
+ context 'for invalid creditor' do
70
+ it 'should fail' do
71
+ expect {
72
+ SEPA::DirectDebit.new.to_xml
73
+ }.to raise_error(SEPA::Error)
74
+ end
75
+ end
76
+
77
+ context 'setting debtor address with adrline' do
78
+ subject do
79
+ sdd = SEPA::DirectDebit.new name: 'Gläubiger GmbH',
80
+ iban: 'DE87200500001234567890',
81
+ creditor_identifier: 'DE98ZZZ09999999999'
82
+
83
+ sda = SEPA::DebtorAddress.new country_code: 'CH',
84
+ address_line1: 'Mustergasse 123',
85
+ address_line2: '1234 Musterstadt'
86
+
87
+ sdd.add_transaction name: 'Zahlemann & Söhne GbR',
88
+ bic: 'SPUEDE2UXXX',
89
+ iban: 'DE21500500009876543210',
90
+ amount: 39.99,
91
+ reference: 'XYZ/2013-08-ABO/12345',
92
+ remittance_information: 'Unsere Rechnung vom 10.08.2013',
93
+ mandate_id: 'K-02-2011-12345',
94
+ debtor_address: sda,
95
+ mandate_date_of_signature: Date.new(2011,1,25)
96
+
97
+ sdd
98
+ end
99
+
100
+ it 'should validate against pain.008.003.02' do
101
+ expect(subject.to_xml(SEPA::PAIN_008_003_02)).to validate_against('pain.008.003.02.xsd')
102
+ end
103
+ end
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
+
135
+ context 'for valid creditor' do
136
+ context 'without BIC (IBAN-only)' do
137
+ subject do
138
+ sdd = SEPA::DirectDebit.new name: 'Gläubiger GmbH',
139
+ iban: 'DE87200500001234567890',
140
+ creditor_identifier: 'DE98ZZZ09999999999'
141
+
142
+ sdd.add_transaction name: 'Zahlemann & Söhne GbR',
143
+ bic: 'SPUEDE2UXXX',
144
+ iban: 'DE21500500009876543210',
145
+ amount: 39.99,
146
+ reference: 'XYZ/2013-08-ABO/12345',
147
+ remittance_information: 'Unsere Rechnung vom 10.08.2013',
148
+ mandate_id: 'K-02-2011-12345',
149
+ mandate_date_of_signature: Date.new(2011,1,25)
150
+
151
+ sdd
152
+ end
153
+
154
+ it 'should validate against pain.008.003.02' do
155
+ expect(subject.to_xml(SEPA::PAIN_008_003_02)).to validate_against('pain.008.003.02.xsd')
156
+ end
157
+
158
+ it 'should fail for pain.008.002.02' do
159
+ expect {
160
+ subject.to_xml(SEPA::PAIN_008_002_02)
161
+ }.to raise_error(SEPA::Error)
162
+ end
163
+
164
+ it 'should validate against pain.008.001.02' do
165
+ expect(subject.to_xml(SEPA::PAIN_008_001_02)).to validate_against('pain.008.001.02.xsd')
166
+ end
167
+ end
168
+
169
+ context 'with BIC' do
170
+ subject do
171
+ sdd = direct_debit
172
+
173
+ sdd.add_transaction name: 'Zahlemann & Söhne GbR',
174
+ bic: 'SPUEDE2UXXX',
175
+ iban: 'DE21500500009876543210',
176
+ amount: 39.99,
177
+ reference: 'XYZ/2013-08-ABO/12345',
178
+ remittance_information: 'Unsere Rechnung vom 10.08.2013',
179
+ mandate_id: 'K-02-2011-12345',
180
+ mandate_date_of_signature: Date.new(2011,1,25)
181
+
182
+ sdd
183
+ end
184
+
185
+ it 'should validate against pain.008.001.02' do
186
+ expect(subject.to_xml(SEPA::PAIN_008_001_02)).to validate_against('pain.008.001.02.xsd')
187
+ end
188
+
189
+ it 'should validate against pain.008.002.02' do
190
+ expect(subject.to_xml(SEPA::PAIN_008_002_02)).to validate_against('pain.008.002.02.xsd')
191
+ end
192
+
193
+ it 'should validate against pain.008.003.02' do
194
+ expect(subject.to_xml(SEPA::PAIN_008_003_02)).to validate_against('pain.008.003.02.xsd')
195
+ end
196
+ end
197
+
198
+ context 'with BIC and debtor address ' do
199
+ subject do
200
+ sdd = direct_debit
201
+
202
+ sda = SEPA::DebtorAddress.new(
203
+ country_code: 'CH',
204
+ address_line1: 'Mustergasse 123',
205
+ address_line2: '1234 Musterstadt'
206
+ )
207
+
208
+ sdd.add_transaction name: 'Zahlemann & Söhne GbR',
209
+ bic: 'SPUEDE2UXXX',
210
+ iban: 'DE21500500009876543210',
211
+ amount: 39.99,
212
+ reference: 'XYZ/2013-08-ABO/12345',
213
+ remittance_information: 'Unsere Rechnung vom 10.08.2013',
214
+ mandate_id: 'K-02-2011-12345',
215
+ debtor_address: sda,
216
+ mandate_date_of_signature: Date.new(2011,1,25)
217
+
218
+ sdd
219
+ end
220
+
221
+ it 'should validate against pain.008.001.02' do
222
+ expect(subject.to_xml(SEPA::PAIN_008_001_02)).to validate_against('pain.008.001.02.xsd')
223
+ end
224
+
225
+ it 'should validate against pain.008.002.02' do
226
+ expect(subject.to_xml(SEPA::PAIN_008_002_02)).to validate_against('pain.008.002.02.xsd')
227
+ end
228
+
229
+ it 'should validate against pain.008.003.02' do
230
+ expect(subject.to_xml(SEPA::PAIN_008_003_02)).to validate_against('pain.008.003.02.xsd')
231
+ end
232
+ end
233
+
234
+ context 'without requested_date given' do
235
+ subject do
236
+ sdd = direct_debit
237
+
238
+ sdd.add_transaction name: 'Zahlemann & Söhne GbR',
239
+ bic: 'SPUEDE2UXXX',
240
+ iban: 'DE21500500009876543210',
241
+ amount: 39.99,
242
+ reference: 'XYZ/2013-08-ABO/12345',
243
+ remittance_information: 'Unsere Rechnung vom 10.08.2013',
244
+ mandate_id: 'K-02-2011-12345',
245
+ mandate_date_of_signature: Date.new(2011,1,25)
246
+
247
+ sdd.add_transaction name: 'Meier & Schulze oHG',
248
+ iban: 'DE68210501700012345678',
249
+ amount: 750.00,
250
+ reference: 'XYZ/2013-08-ABO/6789',
251
+ remittance_information: 'Vielen Dank für Ihren Einkauf!',
252
+ mandate_id: 'K-08-2010-42123',
253
+ mandate_date_of_signature: Date.new(2010,7,25)
254
+
255
+ sdd.to_xml
256
+ end
257
+
258
+ it 'should create valid XML file' do
259
+ expect(subject).to validate_against('pain.008.003.02.xsd')
260
+ end
261
+
262
+ it 'should have creditor identifier' do
263
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/GrpHdr/InitgPty/Id/OrgId/Othr/Id', direct_debit.account.creditor_identifier)
264
+ end
265
+
266
+ it 'should contain <PmtInfId>' do
267
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/PmtInfId', /#{message_id_regex}\/1/)
268
+ end
269
+
270
+ it 'should contain <ReqdColltnDt>' do
271
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/ReqdColltnDt', Date.new(1999, 1, 1).iso8601)
272
+ end
273
+
274
+ it 'should contain <PmtMtd>' do
275
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/PmtMtd', 'DD')
276
+ end
277
+
278
+ it 'should contain <BtchBookg>' do
279
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/BtchBookg', 'true')
280
+ end
281
+
282
+ it 'should contain <NbOfTxs>' do
283
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/NbOfTxs', '2')
284
+ end
285
+
286
+ it 'should contain <CtrlSum>' do
287
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/CtrlSum', '789.99')
288
+ end
289
+
290
+ it 'should contain <Cdtr>' do
291
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/Cdtr/Nm', 'Gläubiger GmbH')
292
+ end
293
+
294
+ it 'should contain <CdtrAcct>' do
295
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/CdtrAcct/Id/IBAN', 'DE87200500001234567890')
296
+ end
297
+
298
+ it 'should contain <CdtrAgt>' do
299
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/CdtrAgt/FinInstnId/BIC', 'BANKDEFFXXX')
300
+ end
301
+
302
+ it 'should contain <CdtrAgt>' do
303
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/CdtrSchmeId/Id/PrvtId/Othr/Id', 'DE98ZZZ09999999999')
304
+ end
305
+
306
+ it 'should contain <EndToEndId>' do
307
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/DrctDbtTxInf[1]/PmtId/EndToEndId', 'XYZ/2013-08-ABO/12345')
308
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/DrctDbtTxInf[2]/PmtId/EndToEndId', 'XYZ/2013-08-ABO/6789')
309
+ end
310
+
311
+ it 'should contain <InstdAmt>' do
312
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/DrctDbtTxInf[1]/InstdAmt', '39.99')
313
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/DrctDbtTxInf[2]/InstdAmt', '750.00')
314
+ end
315
+
316
+ it 'should contain <MndtId>' do
317
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/DrctDbtTxInf[1]/DrctDbtTx/MndtRltdInf/MndtId', 'K-02-2011-12345')
318
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/DrctDbtTxInf[2]/DrctDbtTx/MndtRltdInf/MndtId', 'K-08-2010-42123')
319
+ end
320
+
321
+ it 'should contain <DtOfSgntr>' do
322
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/DrctDbtTxInf[1]/DrctDbtTx/MndtRltdInf/DtOfSgntr', '2011-01-25')
323
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/DrctDbtTxInf[2]/DrctDbtTx/MndtRltdInf/DtOfSgntr', '2010-07-25')
324
+ end
325
+
326
+ it 'should contain <DbtrAgt>' do
327
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/DrctDbtTxInf[1]/DbtrAgt/FinInstnId/BIC', 'SPUEDE2UXXX')
328
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/DrctDbtTxInf[2]/DbtrAgt/FinInstnId/Othr/Id', 'NOTPROVIDED')
329
+ end
330
+
331
+ it 'should contain <Dbtr>' do
332
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/DrctDbtTxInf[1]/Dbtr/Nm', 'Zahlemann & Söhne GbR')
333
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/DrctDbtTxInf[2]/Dbtr/Nm', 'Meier & Schulze oHG')
334
+ end
335
+
336
+ it 'should contain <DbtrAcct>' do
337
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/DrctDbtTxInf[1]/DbtrAcct/Id/IBAN', 'DE21500500009876543210')
338
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/DrctDbtTxInf[2]/DbtrAcct/Id/IBAN', 'DE68210501700012345678')
339
+ end
340
+
341
+ it 'should contain <RmtInf>' do
342
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/DrctDbtTxInf[1]/RmtInf/Ustrd', 'Unsere Rechnung vom 10.08.2013')
343
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/DrctDbtTxInf[2]/RmtInf/Ustrd', 'Vielen Dank für Ihren Einkauf')
344
+ end
345
+ end
346
+
347
+ context 'with different requested_date given' do
348
+ subject do
349
+ sdd = direct_debit
350
+
351
+ sdd.add_transaction(direct_debt_transaction.merge requested_date: Date.today + 1)
352
+ sdd.add_transaction(direct_debt_transaction.merge requested_date: Date.today + 2)
353
+ sdd.add_transaction(direct_debt_transaction.merge requested_date: Date.today + 2)
354
+
355
+ sdd.to_xml
356
+ end
357
+
358
+ it 'should contain two payment_informations with <ReqdColltnDt>' do
359
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf[1]/ReqdColltnDt', (Date.today + 1).iso8601)
360
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf[2]/ReqdColltnDt', (Date.today + 2).iso8601)
361
+
362
+ expect(subject).not_to have_xml('//Document/CstmrDrctDbtInitn/PmtInf[3]')
363
+ end
364
+
365
+ it 'should contain two payment_informations with different <PmtInfId>' do
366
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf[1]/PmtInfId', /#{message_id_regex}\/1/)
367
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf[2]/PmtInfId', /#{message_id_regex}\/2/)
368
+ end
369
+ end
370
+
371
+ context 'with different local_instrument given' do
372
+ subject do
373
+ sdd = direct_debit
374
+
375
+ sdd.add_transaction(direct_debt_transaction.merge local_instrument: 'CORE')
376
+ sdd.add_transaction(direct_debt_transaction.merge local_instrument: 'B2B')
377
+
378
+ sdd
379
+ end
380
+
381
+ it 'should have errors' do
382
+ expect(subject.errors_on(:base).size).to eq(1)
383
+ end
384
+
385
+ it 'should raise error on XML generation' do
386
+ expect {
387
+ subject.to_xml
388
+ }.to raise_error(SEPA::Error)
389
+ end
390
+ end
391
+
392
+ context 'with different sequence_type given' do
393
+ subject do
394
+ sdd = direct_debit
395
+
396
+ sdd.add_transaction(direct_debt_transaction.merge sequence_type: 'OOFF')
397
+ sdd.add_transaction(direct_debt_transaction.merge sequence_type: 'FRST')
398
+ sdd.add_transaction(direct_debt_transaction.merge sequence_type: 'FRST')
399
+
400
+ sdd.to_xml
401
+ end
402
+
403
+ it 'should contain two payment_informations with <LclInstrm>' do
404
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf[1]/PmtTpInf/SeqTp', 'OOFF')
405
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf[2]/PmtTpInf/SeqTp', 'FRST')
406
+
407
+ expect(subject).not_to have_xml('//Document/CstmrDrctDbtInitn/PmtInf[3]')
408
+ end
409
+ end
410
+
411
+ context 'with different batch_booking given' do
412
+ subject do
413
+ sdd = direct_debit
414
+
415
+ sdd.add_transaction(direct_debt_transaction.merge batch_booking: false)
416
+ sdd.add_transaction(direct_debt_transaction.merge batch_booking: true)
417
+ sdd.add_transaction(direct_debt_transaction.merge batch_booking: true)
418
+
419
+ sdd.to_xml
420
+ end
421
+
422
+ it 'should contain two payment_informations with <BtchBookg>' do
423
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf[1]/BtchBookg', 'false')
424
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf[2]/BtchBookg', 'true')
425
+
426
+ expect(subject).not_to have_xml('//Document/CstmrDrctDbtInitn/PmtInf[3]')
427
+ end
428
+ end
429
+
430
+ context 'with transactions containing different group criteria' do
431
+ subject do
432
+ sdd = direct_debit
433
+
434
+ sdd.add_transaction(direct_debt_transaction.merge requested_date: Date.today + 1, sequence_type: 'OOFF', amount: 1)
435
+ sdd.add_transaction(direct_debt_transaction.merge requested_date: Date.today + 1, sequence_type: 'FNAL', amount: 2)
436
+ sdd.add_transaction(direct_debt_transaction.merge requested_date: Date.today + 2, sequence_type: 'OOFF', amount: 4)
437
+ sdd.add_transaction(direct_debt_transaction.merge requested_date: Date.today + 2, sequence_type: 'FNAL', amount: 8)
438
+
439
+ sdd.to_xml
440
+ end
441
+
442
+ it 'should contain multiple payment_informations' do
443
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf[1]/ReqdColltnDt', (Date.today + 1).iso8601)
444
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf[1]/PmtTpInf/SeqTp', 'OOFF')
445
+
446
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf[2]/ReqdColltnDt', (Date.today + 1).iso8601)
447
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf[2]/PmtTpInf/SeqTp', 'FNAL')
448
+
449
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf[3]/ReqdColltnDt', (Date.today + 2).iso8601)
450
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf[3]/PmtTpInf/SeqTp', 'OOFF')
451
+
452
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf[4]/ReqdColltnDt', (Date.today + 2).iso8601)
453
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf[4]/PmtTpInf/SeqTp', 'FNAL')
454
+ end
455
+
456
+ it 'should have multiple control sums' do
457
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf[1]/CtrlSum', '1.00')
458
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf[2]/CtrlSum', '2.00')
459
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf[3]/CtrlSum', '4.00')
460
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf[4]/CtrlSum', '8.00')
461
+ end
462
+ end
463
+
464
+ context 'with transactions containing different creditor_account' do
465
+ subject do
466
+ sdd = direct_debit
467
+
468
+ sdd.add_transaction(direct_debt_transaction)
469
+ sdd.add_transaction(direct_debt_transaction.merge(creditor_account: SEPA::CreditorAccount.new(
470
+ name: 'Creditor Inc.',
471
+ bic: 'RABONL2U',
472
+ iban: 'NL08RABO0135742099',
473
+ creditor_identifier: 'NL53ZZZ091734220000'))
474
+ )
475
+
476
+ sdd.to_xml
477
+ end
478
+
479
+ it 'should contain two payment_informations with <Cdtr>' do
480
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf[1]/Cdtr/Nm', 'Gläubiger GmbH')
481
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf[2]/Cdtr/Nm', 'Creditor Inc.')
482
+ end
483
+ end
484
+
485
+ context 'with mandate amendments' do
486
+ subject do
487
+ sdd = direct_debit
488
+
489
+ sdd.add_transaction(direct_debt_transaction.merge(original_debtor_account: 'NL08RABO0135742099'))
490
+ sdd.add_transaction(direct_debt_transaction.merge(same_mandate_new_debtor_agent: true))
491
+ sdd.add_transaction(direct_debt_transaction.merge(original_creditor_account: SEPA::CreditorAccount.new(creditor_identifier: 'NL53ZZZ091734220000', name: 'Creditor Inc.')))
492
+ sdd.to_xml
493
+ end
494
+
495
+ it 'should include amendment indicator' do
496
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/DrctDbtTxInf[1]/DrctDbtTx/MndtRltdInf/AmdmntInd', 'true')
497
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/DrctDbtTxInf[2]/DrctDbtTx/MndtRltdInf/AmdmntInd', 'true')
498
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/DrctDbtTxInf[3]/DrctDbtTx/MndtRltdInf/AmdmntInd', 'true')
499
+ end
500
+
501
+ it 'should include amendment information details' do
502
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/DrctDbtTxInf[1]/DrctDbtTx/MndtRltdInf/AmdmntInfDtls/OrgnlDbtrAcct/Id/IBAN', 'NL08RABO0135742099')
503
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/DrctDbtTxInf[2]/DrctDbtTx/MndtRltdInf/AmdmntInfDtls/OrgnlDbtrAgt/FinInstnId/Othr/Id', 'SMNDA')
504
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/DrctDbtTxInf[3]/DrctDbtTx/MndtRltdInf/AmdmntInfDtls/OrgnlCdtrSchmeId/Nm', 'Creditor Inc.')
505
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/DrctDbtTxInf[3]/DrctDbtTx/MndtRltdInf/AmdmntInfDtls/OrgnlCdtrSchmeId/Id/PrvtId/Othr/Id', 'NL53ZZZ091734220000')
506
+ end
507
+ end
508
+
509
+ context 'with instruction given' do
510
+ subject do
511
+ sct = direct_debit
512
+
513
+ sct.add_transaction(direct_debt_transaction.merge(instruction: '1234/ABC'))
514
+
515
+ sct.to_xml
516
+ end
517
+
518
+ it 'should create valid XML file' do
519
+ expect(subject).to validate_against('pain.008.003.02.xsd')
520
+ end
521
+
522
+ it 'should contain <InstrId>' do
523
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/DrctDbtTxInf[1]/PmtId/InstrId', '1234/ABC')
524
+ end
525
+ end
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
+
540
+ context 'with a different currency given' do
541
+ subject do
542
+ sct = direct_debit
543
+
544
+ sct.add_transaction(direct_debt_transaction.merge(instruction: '1234/ABC', currency: 'SEK'))
545
+
546
+ sct
547
+ end
548
+
549
+ it 'should validate against pain.001.001.03' do
550
+ expect(subject.to_xml(SEPA::PAIN_008_001_02)).to validate_against('pain.008.001.02.xsd')
551
+ end
552
+
553
+ it 'should have a CHF Ccy' do
554
+ doc = Nokogiri::XML(subject.to_xml('pain.008.001.02'))
555
+ doc.remove_namespaces!
556
+
557
+ nodes = doc.xpath('//Document/CstmrDrctDbtInitn/PmtInf/DrctDbtTxInf[1]/InstdAmt')
558
+ expect(nodes.length).to eql(1)
559
+ expect(nodes.first.attribute('Ccy').value).to eql('SEK')
560
+ end
561
+
562
+ it 'should fail for pain.008.002.02' do
563
+ expect {
564
+ subject.to_xml(SEPA::PAIN_008_002_02)
565
+ }.to raise_error(SEPA::Error)
566
+ end
567
+
568
+ it 'should fail for pain.008.003.02' do
569
+ expect {
570
+ subject.to_xml(SEPA::PAIN_008_003_02)
571
+ }.to raise_error(SEPA::Error)
572
+ end
573
+ end
574
+ end
575
+
576
+ context 'xml_schema_header' do
577
+ subject { sepa_direct_debit.to_xml(format) }
578
+
579
+ let(:sepa_direct_debit) do
580
+ SEPA::DirectDebit.new name: 'Gläubiger GmbH',
581
+ iban: 'DE87200500001234567890',
582
+ creditor_identifier: 'DE98ZZZ09999999999'
583
+ end
584
+
585
+ let(:xml_header) do
586
+ '<?xml version="1.0" encoding="UTF-8"?>' +
587
+ "\n<Document xmlns=\"urn:iso:std:iso:20022:tech:xsd:#{format}\"" +
588
+ ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"' +
589
+ " xsi:schemaLocation=\"urn:iso:std:iso:20022:tech:xsd:#{format} #{format}.xsd\">\n"
590
+ end
591
+
592
+ let(:transaction) do
593
+ {
594
+ name: 'Zahlemann & Söhne GbR',
595
+ bic: 'SPUEDE2UXXX',
596
+ iban: 'DE21500500009876543210',
597
+ amount: 39.99,
598
+ reference: 'XYZ/2013-08-ABO/12345',
599
+ remittance_information: 'Unsere Rechnung vom 10.08.2013',
600
+ mandate_id: 'K-02-2011-12345',
601
+ mandate_date_of_signature: Date.new(2011, 1, 25)
602
+ }
603
+ end
604
+
605
+ before do
606
+ sepa_direct_debit.add_transaction transaction
607
+ end
608
+
609
+ context "when format is #{SEPA::PAIN_008_001_02}" do
610
+ let(:format) { SEPA::PAIN_008_001_02 }
611
+
612
+ it 'should return correct header' do
613
+ is_expected.to start_with(xml_header)
614
+ end
615
+ end
616
+
617
+ context "when format is #{SEPA::PAIN_008_002_02}" do
618
+ let(:format) { SEPA::PAIN_008_002_02 }
619
+ let(:sepa_direct_debit) do
620
+ SEPA::DirectDebit.new name: 'Gläubiger GmbH',
621
+ bic: 'SPUEDE2UXXX',
622
+ iban: 'DE87200500001234567890',
623
+ creditor_identifier: 'DE98ZZZ09999999999'
624
+ end
625
+ let(:transaction) do
626
+ {
627
+ name: 'Zahlemann & Söhne GbR',
628
+ bic: 'SPUEDE2UXXX',
629
+ iban: 'DE21500500009876543210',
630
+ amount: 39.99,
631
+ reference: 'XYZ/2013-08-ABO/12345',
632
+ remittance_information: 'Unsere Rechnung vom 10.08.2013',
633
+ mandate_id: 'K-02-2011-12345',
634
+ debtor_address: SEPA::DebtorAddress.new(
635
+ country_code: 'CH',
636
+ address_line1: 'Mustergasse 123',
637
+ address_line2: '1234 Musterstadt'
638
+ ),
639
+ mandate_date_of_signature: Date.new(2011, 1, 25)
640
+ }
641
+ end
642
+
643
+ it 'should return correct header' do
644
+ is_expected.to start_with(xml_header)
645
+ end
646
+ end
647
+
648
+ context "when format is #{SEPA::PAIN_008_003_02}" do
649
+ let(:format) { SEPA::PAIN_008_003_02 }
650
+
651
+ it 'should return correct header' do
652
+ is_expected.to start_with(xml_header)
653
+ end
654
+ end
655
+ end
656
+ end
657
+ end