sps_king 0.1.0

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.
Files changed (60) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +32 -0
  5. data/CONTRIBUTING.md +38 -0
  6. data/Gemfile +2 -0
  7. data/LICENSE.txt +24 -0
  8. data/README.md +246 -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/lib/schema/pain.001.001.03.ch.02.xsd +1212 -0
  19. data/lib/schema/pain.008.001.02.ch.03.xsd +784 -0
  20. data/lib/sps_king.rb +22 -0
  21. data/lib/sps_king/account.rb +24 -0
  22. data/lib/sps_king/account/creditor_account.rb +11 -0
  23. data/lib/sps_king/account/creditor_address.rb +37 -0
  24. data/lib/sps_king/account/debtor_account.rb +5 -0
  25. data/lib/sps_king/account/debtor_address.rb +34 -0
  26. data/lib/sps_king/converter.rb +53 -0
  27. data/lib/sps_king/error.rb +4 -0
  28. data/lib/sps_king/message.rb +163 -0
  29. data/lib/sps_king/message/credit_transfer.rb +140 -0
  30. data/lib/sps_king/message/direct_debit.rb +177 -0
  31. data/lib/sps_king/structured_remittance_information.rb +20 -0
  32. data/lib/sps_king/transaction.rb +59 -0
  33. data/lib/sps_king/transaction/credit_transfer_transaction.rb +19 -0
  34. data/lib/sps_king/transaction/direct_debit_transaction.rb +58 -0
  35. data/lib/sps_king/validator.rb +55 -0
  36. data/lib/sps_king/version.rb +3 -0
  37. data/spec/examples/pain.001.001.03.ch.02.xml +172 -0
  38. data/spec/examples/pain.008.001.02.ch.03.xml +240 -0
  39. data/spec/lib/sps_king/account/creditor_account_spec.rb +52 -0
  40. data/spec/lib/sps_king/account/creditor_address_spec.rb +14 -0
  41. data/spec/lib/sps_king/account/debtor_account_spec.rb +14 -0
  42. data/spec/lib/sps_king/account/debtor_address_spec.rb +14 -0
  43. data/spec/lib/sps_king/account_spec.rb +42 -0
  44. data/spec/lib/sps_king/converter_spec.rb +88 -0
  45. data/spec/lib/sps_king/message/credit_transfer_spec.rb +350 -0
  46. data/spec/lib/sps_king/message/direct_debit_spec.rb +483 -0
  47. data/spec/lib/sps_king/message_spec.rb +128 -0
  48. data/spec/lib/sps_king/structured_remittance_information_spec.rb +32 -0
  49. data/spec/lib/sps_king/transaction/credit_transfer_transaction_spec.rb +53 -0
  50. data/spec/lib/sps_king/transaction/direct_debit_transaction_spec.rb +56 -0
  51. data/spec/lib/sps_king/transaction_spec.rb +133 -0
  52. data/spec/lib/sps_king/validation_spec.rb +23 -0
  53. data/spec/lib/sps_king/validator_spec.rb +78 -0
  54. data/spec/spec_helper.rb +33 -0
  55. data/spec/support/active_model.rb +30 -0
  56. data/spec/support/custom_matcher.rb +60 -0
  57. data/spec/support/factories.rb +33 -0
  58. data/spec/support/validations.rb +27 -0
  59. data/sps_king.gemspec +31 -0
  60. metadata +237 -0
@@ -0,0 +1,350 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe SPS::CreditTransfer do
5
+ let(:message_id_regex) { /SPS-KING\/[0-9a-z_]{22}/ }
6
+ let(:credit_transfer) {
7
+ SPS::CreditTransfer.new(
8
+ name: 'Schuldner GmbH',
9
+ bic: 'RAIFCH22',
10
+ iban: 'CH5481230000001998736'
11
+ )
12
+ }
13
+
14
+ describe :new do
15
+ it 'should accept missing options' do
16
+ expect {
17
+ SPS::CreditTransfer.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
+ credit_transfer.add_transaction(credit_transfer_transaction)
26
+ end
27
+
28
+ expect(credit_transfer.transactions.size).to eq(3)
29
+ end
30
+
31
+ it 'should fail for invalid transaction' do
32
+ expect {
33
+ credit_transfer.add_transaction name: ''
34
+ }.to raise_error(ArgumentError)
35
+ end
36
+ end
37
+
38
+ describe :to_xml do
39
+ context 'for invalid debtor' do
40
+ it 'should fail' do
41
+ expect {
42
+ SPS::CreditTransfer.new.to_xml
43
+ }.to raise_error(SPS::Error)
44
+ end
45
+ end
46
+
47
+ context 'setting creditor address with adrline' do
48
+ subject do
49
+ sct = SPS::CreditTransfer.new(
50
+ name: 'Schuldner GmbH',
51
+ iban: 'CH5481230000001998736',
52
+ bic: 'RAIFCH22'
53
+ )
54
+
55
+ sca = SPS::CreditorAddress.new(
56
+ country_code: 'CH',
57
+ address_line1: 'Mustergasse 123',
58
+ address_line2: '1234 Musterstadt'
59
+ )
60
+
61
+ sct.add_transaction(
62
+ name: 'Contoso AG',
63
+ bic: 'CRESCHZZ80A',
64
+ iban: 'CH9300762011623852957',
65
+ currency: 'CHF',
66
+ amount: 102.50,
67
+ reference: 'XYZ-1234/123',
68
+ remittance_information: 'Rechnung vom 22.08.2013',
69
+ creditor_address: sca
70
+ )
71
+
72
+ sct
73
+ end
74
+
75
+ it 'should validate against pain.001.001.03.ch.02' do
76
+ expect(
77
+ subject.to_xml(SPS::PAIN_001_001_03_CH_02)
78
+ ).to validate_against('pain.001.001.03.ch.02.xsd')
79
+ end
80
+ end
81
+
82
+ context 'setting creditor address with structured fields' do
83
+ subject do
84
+ sct = SPS::CreditTransfer.new(
85
+ name: 'Schuldner GmbH',
86
+ iban: 'CH5481230000001998736',
87
+ bic: 'RAIFCH22'
88
+ )
89
+
90
+ sca = SPS::CreditorAddress.new(
91
+ country_code: 'CH',
92
+ street_name: 'Mustergasse',
93
+ building_number: '123',
94
+ post_code: '1234',
95
+ town_name: 'Musterstadt'
96
+ )
97
+
98
+ sct.add_transaction(
99
+ name: 'Contoso AG',
100
+ bic: 'CRESCHZZ80A',
101
+ iban: 'CH9300762011623852957',
102
+ amount: 102.50,
103
+ reference: 'XYZ-1234/123',
104
+ remittance_information: 'Rechnung vom 22.08.2013',
105
+ creditor_address: sca
106
+ )
107
+
108
+ sct
109
+ end
110
+
111
+ it "should validate against pain.001.001.03.ch.02" do
112
+ expect(subject.to_xml(SPS::PAIN_001_001_03_CH_02))
113
+ .to validate_against("pain.001.001.03.ch.02.xsd")
114
+ end
115
+ end
116
+
117
+ context 'for valid debtor' do
118
+ context 'with BIC' do
119
+ subject do
120
+ sct = credit_transfer
121
+
122
+ sct.add_transaction(
123
+ name: 'Contoso AG',
124
+ bic: 'CRESCHZZ80A',
125
+ iban: 'CH9300762011623852957',
126
+ service_level: 'SEPA',
127
+ amount: 102.50,
128
+ reference: 'XYZ-1234/123',
129
+ remittance_information: 'Rechnung vom 22.08.2013'
130
+ )
131
+
132
+ sct
133
+ end
134
+
135
+ it 'should validate against pain.001.001.03.ch.02' do
136
+ expect(subject.to_xml('pain.001.001.03.ch.02')).to validate_against('pain.001.001.03.ch.02.xsd')
137
+ end
138
+ end
139
+
140
+ context 'without requested_date given' do
141
+ subject do
142
+ sct = credit_transfer
143
+
144
+ sct.add_transaction(
145
+ name: 'Contoso AG',
146
+ bic: 'CRESCHZZ80A',
147
+ iban: 'CH9300762011623852957',
148
+ amount: 102.50,
149
+ reference: 'XYZ-1234/123',
150
+ remittance_information: 'Rechnung vom 22.08.2013'
151
+ )
152
+
153
+ sct.add_transaction(
154
+ name: 'Amazonas GmbH',
155
+ bic: 'RAIFCH22C32',
156
+ iban: 'CH7081232000001998736',
157
+ amount: 59.00,
158
+ reference: 'XYZ-5678/456',
159
+ remittance_information: 'Rechnung vom 21.08.2013'
160
+ )
161
+
162
+ sct.to_xml
163
+ end
164
+
165
+ it 'should create valid XML file' do
166
+ expect(subject).to validate_against('pain.001.001.03.ch.02.xsd')
167
+ end
168
+
169
+ it 'should have message_identification' do
170
+ expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/GrpHdr/MsgId', message_id_regex)
171
+ end
172
+
173
+ it 'should contain <PmtInfId>' do
174
+ expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf/PmtInfId', /#{message_id_regex}\/1/)
175
+ end
176
+
177
+ it 'should contain <ReqdExctnDt>' do
178
+ expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf/ReqdExctnDt', Date.new(1999, 1, 1).iso8601)
179
+ end
180
+
181
+ it 'should contain <PmtMtd>' do
182
+ expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf/PmtMtd', 'TRF')
183
+ end
184
+
185
+ it 'should contain <BtchBookg>' do
186
+ expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf/BtchBookg', 'true')
187
+ end
188
+
189
+ it 'should contain <NbOfTxs>' do
190
+ expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf/NbOfTxs', '2')
191
+ end
192
+
193
+ it 'should contain <CtrlSum>' do
194
+ expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf/CtrlSum', '161.50')
195
+ end
196
+
197
+ it 'should contain <Dbtr>' do
198
+ expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf/Dbtr/Nm', 'Schuldner GmbH')
199
+ end
200
+
201
+ it 'should contain <DbtrAcct>' do
202
+ expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf/DbtrAcct/Id/IBAN', 'CH5481230000001998736')
203
+ end
204
+
205
+ it 'should contain <DbtrAgt>' do
206
+ expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf/DbtrAgt/FinInstnId/BIC', 'RAIFCH22')
207
+ end
208
+
209
+ it 'should contain <EndToEndId>' do
210
+ expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf/CdtTrfTxInf[1]/PmtId/EndToEndId', 'XYZ-1234/123')
211
+ expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf/CdtTrfTxInf[2]/PmtId/EndToEndId', 'XYZ-5678/456')
212
+ end
213
+
214
+ it 'should contain <Amt>' do
215
+ expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf/CdtTrfTxInf[1]/Amt/InstdAmt', '102.50')
216
+ expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf/CdtTrfTxInf[2]/Amt/InstdAmt', '59.00')
217
+ end
218
+
219
+ it 'should contain <CdtrAgt> for every BIC given' do
220
+ expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf/CdtTrfTxInf[1]/CdtrAgt/FinInstnId/BIC', 'CRESCHZZ80A')
221
+ expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf/CdtTrfTxInf[2]/CdtrAgt/FinInstnId/BIC', 'RAIFCH22C32')
222
+ end
223
+
224
+ it 'should contain <Cdtr>' do
225
+ expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf/CdtTrfTxInf[1]/Cdtr/Nm', 'Contoso AG')
226
+ expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf/CdtTrfTxInf[2]/Cdtr/Nm', 'Amazonas GmbH')
227
+ end
228
+
229
+ it 'should contain <CdtrAcct>' do
230
+ expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf/CdtTrfTxInf[1]/CdtrAcct/Id/IBAN', 'CH9300762011623852957')
231
+ expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf/CdtTrfTxInf[2]/CdtrAcct/Id/IBAN', 'CH7081232000001998736')
232
+ end
233
+
234
+ it 'should contain <RmtInf>' do
235
+ expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf/CdtTrfTxInf[1]/RmtInf/Ustrd', 'Rechnung vom 22.08.2013')
236
+ expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf/CdtTrfTxInf[2]/RmtInf/Ustrd', 'Rechnung vom 21.08.2013')
237
+ end
238
+ end
239
+
240
+ context 'with different requested_date given' do
241
+ subject do
242
+ sct = credit_transfer
243
+
244
+ sct.add_transaction(credit_transfer_transaction.merge requested_date: Date.today + 1)
245
+ sct.add_transaction(credit_transfer_transaction.merge requested_date: Date.today + 2)
246
+ sct.add_transaction(credit_transfer_transaction.merge requested_date: Date.today + 2)
247
+
248
+ sct.to_xml
249
+ end
250
+
251
+ it 'should contain two payment_informations with <ReqdExctnDt>' do
252
+ expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf[1]/ReqdExctnDt', (Date.today + 1).iso8601)
253
+ expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf[2]/ReqdExctnDt', (Date.today + 2).iso8601)
254
+
255
+ expect(subject).not_to have_xml('//Document/CstmrCdtTrfInitn/PmtInf[3]')
256
+ end
257
+
258
+ it 'should contain two payment_informations with different <PmtInfId>' do
259
+ expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf[1]/PmtInfId', /#{message_id_regex}\/1/)
260
+ expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf[2]/PmtInfId', /#{message_id_regex}\/2/)
261
+ end
262
+ end
263
+
264
+ context 'with different batch_booking given' do
265
+ subject do
266
+ sct = credit_transfer
267
+
268
+ sct.add_transaction(credit_transfer_transaction.merge batch_booking: false)
269
+ sct.add_transaction(credit_transfer_transaction.merge batch_booking: true)
270
+ sct.add_transaction(credit_transfer_transaction.merge batch_booking: true)
271
+
272
+ sct.to_xml
273
+ end
274
+
275
+ it 'should contain two payment_informations with <BtchBookg>' do
276
+ expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf[1]/BtchBookg', 'false')
277
+ expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf[2]/BtchBookg', 'true')
278
+
279
+ expect(subject).not_to have_xml('//Document/CstmrCdtTrfInitn/PmtInf[3]')
280
+ end
281
+ end
282
+
283
+ context 'with transactions containing different group criteria' do
284
+ subject do
285
+ sct = credit_transfer
286
+
287
+ sct.add_transaction(credit_transfer_transaction.merge requested_date: Date.today + 1, batch_booking: false, amount: 1)
288
+ sct.add_transaction(credit_transfer_transaction.merge requested_date: Date.today + 1, batch_booking: true, amount: 2)
289
+ sct.add_transaction(credit_transfer_transaction.merge requested_date: Date.today + 2, batch_booking: false, amount: 4)
290
+ sct.add_transaction(credit_transfer_transaction.merge requested_date: Date.today + 2, batch_booking: true, amount: 8)
291
+ sct.add_transaction(credit_transfer_transaction.merge requested_date: Date.today + 2, batch_booking: true, category_purpose: 'SALA', amount: 6)
292
+
293
+ sct.to_xml
294
+ end
295
+
296
+ it 'should contain multiple payment_informations' do
297
+ expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf[1]/ReqdExctnDt', (Date.today + 1).iso8601)
298
+ expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf[1]/BtchBookg', 'false')
299
+
300
+ expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf[2]/ReqdExctnDt', (Date.today + 1).iso8601)
301
+ expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf[2]/BtchBookg', 'true')
302
+
303
+ expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf[3]/ReqdExctnDt', (Date.today + 2).iso8601)
304
+ expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf[3]/BtchBookg', 'false')
305
+
306
+ expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf[4]/ReqdExctnDt', (Date.today + 2).iso8601)
307
+ expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf[4]/BtchBookg', 'true')
308
+
309
+ expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf[5]/ReqdExctnDt', (Date.today + 2).iso8601)
310
+ expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf[5]/PmtTpInf/CtgyPurp/Cd', 'SALA')
311
+ end
312
+
313
+ it 'should have multiple control sums' do
314
+ expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf[1]/CtrlSum', '1.00')
315
+ expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf[2]/CtrlSum', '2.00')
316
+ expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf[3]/CtrlSum', '4.00')
317
+ expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf[4]/CtrlSum', '8.00')
318
+ end
319
+ end
320
+
321
+ context 'with instruction given' do
322
+ subject do
323
+ sct = credit_transfer
324
+
325
+ sct.add_transaction(
326
+ name: 'Contoso AG',
327
+ iban: 'CH5481230000001998736',
328
+ bic: 'RAIFCH22',
329
+ amount: 102.50,
330
+ instruction: '1234/ABC'
331
+ )
332
+
333
+ sct.to_xml
334
+ end
335
+
336
+ it 'should create valid XML file' do
337
+ expect(subject).to validate_against('pain.001.001.03.ch.02.xsd')
338
+ end
339
+
340
+ it 'should contain <InstrId>' do
341
+ expect(subject)
342
+ .to have_xml(
343
+ '//Document/CstmrCdtTrfInitn/PmtInf/CdtTrfTxInf[1]/PmtId/InstrId',
344
+ '1234/ABC'
345
+ )
346
+ end
347
+ end
348
+ end
349
+ end
350
+ end
@@ -0,0 +1,483 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe SPS::DirectDebit do
5
+ let(:message_id_regex) { /SPS-KING\/[0-9a-z_]{22}/ }
6
+
7
+ let(:direct_debit) {
8
+ SPS::DirectDebit.new(
9
+ name: 'Gläubiger GmbH',
10
+ iban: 'CH5481230000001998736',
11
+ creditor_identifier: 'DE98ZZZ09999999999'
12
+ )
13
+ }
14
+
15
+ describe :new do
16
+ it 'should accept missing options' do
17
+ expect {
18
+ SPS::DirectDebit.new
19
+ }.to_not raise_error
20
+ end
21
+ end
22
+
23
+ describe :add_transaction do
24
+ it 'should add valid transactions' do
25
+ 3.times do
26
+ direct_debit.add_transaction(direct_debt_transaction)
27
+ end
28
+
29
+ expect(direct_debit.transactions.size).to eq(3)
30
+ end
31
+
32
+ it 'should fail for invalid transaction' do
33
+ expect {
34
+ direct_debit.add_transaction name: ''
35
+ }.to raise_error(ArgumentError)
36
+ end
37
+ end
38
+
39
+ describe :batch_id do
40
+ it 'returns the id of the batch where the given transactions belongs to (1 batch)' do
41
+ direct_debit.add_transaction(direct_debt_transaction(reference: "EXAMPLE REFERENCE"))
42
+
43
+ expect(direct_debit.batch_id("EXAMPLE REFERENCE")).to match(/#{message_id_regex}\/1/)
44
+ end
45
+
46
+ it 'returns the id of the batch where the given transactions belongs to (2 batches)' do
47
+ direct_debit.add_transaction(direct_debt_transaction(reference: "EXAMPLE REFERENCE 1"))
48
+ direct_debit.add_transaction(direct_debt_transaction(reference: "EXAMPLE REFERENCE 2", requested_date: Date.today.next.next))
49
+ direct_debit.add_transaction(direct_debt_transaction(reference: "EXAMPLE REFERENCE 3"))
50
+
51
+ expect(direct_debit.batch_id("EXAMPLE REFERENCE 1")).to match(/#{message_id_regex}\/1/)
52
+ expect(direct_debit.batch_id("EXAMPLE REFERENCE 2")).to match(/#{message_id_regex}\/2/)
53
+ expect(direct_debit.batch_id("EXAMPLE REFERENCE 3")).to match(/#{message_id_regex}\/1/)
54
+ end
55
+ end
56
+
57
+ describe :batches do
58
+ it 'returns an array of batch ids in the sepa message' do
59
+ direct_debit.add_transaction(direct_debt_transaction(reference: "EXAMPLE REFERENCE 1"))
60
+ direct_debit.add_transaction(direct_debt_transaction(reference: "EXAMPLE REFERENCE 2", requested_date: Date.today.next.next))
61
+ direct_debit.add_transaction(direct_debt_transaction(reference: "EXAMPLE REFERENCE 3"))
62
+
63
+ expect(direct_debit.batches.size).to eq(2)
64
+ expect(direct_debit.batches[0]).to match(/#{message_id_regex}\/[0-9]+/)
65
+ expect(direct_debit.batches[1]).to match(/#{message_id_regex}\/[0-9]+/)
66
+ end
67
+ end
68
+
69
+ describe :to_xml do
70
+ context 'for invalid creditor' do
71
+ it 'should fail' do
72
+ expect {
73
+ SPS::DirectDebit.new.to_xml
74
+ }.to raise_error(SPS::Error)
75
+ end
76
+ end
77
+
78
+ context 'setting debtor address with adrline' do
79
+ subject do
80
+ sdd = SPS::DirectDebit.new(
81
+ name: 'Gläubiger GmbH',
82
+ iban: 'CH7081232000001998736',
83
+ creditor_identifier: 'ABC1W'
84
+ )
85
+
86
+ sda = SPS::DebtorAddress.new(
87
+ country_code: 'CH',
88
+ address_line1: 'Mustergasse 123',
89
+ address_line2: '1234 Musterstadt'
90
+ )
91
+
92
+ sdd.add_transaction(
93
+ name: 'Zahlemann & Söhne GbR',
94
+ iban: 'CH9804835011062385295',
95
+ amount: 39.99,
96
+ instruction: '12',
97
+ reference: 'XYZ/2013-08-ABO/12345',
98
+ remittance_information: 'Unsere Rechnung vom 10.08.2013',
99
+ debtor_address: sda,
100
+ structured_remittance_information: structured_remittance_information
101
+ )
102
+
103
+ sdd
104
+ end
105
+
106
+ it 'should validate against pain.008.001.02.ch.03' do
107
+ expect(subject.to_xml(SPS::PAIN_008_001_02_CH_03))
108
+ .to validate_against('pain.008.001.02.ch.03.xsd')
109
+ end
110
+ end
111
+
112
+ context 'setting debtor address with structured fields' do
113
+ subject do
114
+ sdd = SPS::DirectDebit.new(
115
+ name: 'Gläubiger GmbH',
116
+ iban: 'CH7081232000001998736',
117
+ creditor_identifier: 'ABC1W'
118
+ )
119
+
120
+ sda = SPS::DebtorAddress.new(
121
+ country_code: 'CH',
122
+ street_name: 'Mustergasse 123',
123
+ post_code: '1234',
124
+ town_name: 'Musterstadt'
125
+ )
126
+
127
+ sdd.add_transaction(
128
+ name: 'Zahlemann & Söhne GbR',
129
+ iban: 'CH9804835011062385295',
130
+ amount: 39.99,
131
+ instruction: '12',
132
+ reference: 'XYZ/2013-08-ABO/12345',
133
+ remittance_information: 'Unsere Rechnung vom 10.08.2013',
134
+ debtor_address: sda,
135
+ structured_remittance_information: structured_remittance_information
136
+ )
137
+
138
+ sdd
139
+ end
140
+
141
+ it 'should validate against pain.008.001.02.ch.03' do
142
+ expect(subject.to_xml(SPS::PAIN_008_001_02_CH_03))
143
+ .to validate_against('pain.008.001.02.ch.03.xsd')
144
+ end
145
+ end
146
+
147
+ context 'for valid creditor' do
148
+ context 'for swiss direct debits' do
149
+ let(:creditor_iban) { 'CH7081232000001998736' }
150
+ let(:debtior_iban) { 'CH9804835011062385295' }
151
+
152
+ let(:direct_debit) do
153
+ sdd = SPS::DirectDebit.new(
154
+ name: 'Muster AG',
155
+ isr_participant_number: '010001456',
156
+ iban: creditor_iban,
157
+ creditor_identifier: 'ABC1W'
158
+ )
159
+
160
+ sda = SPS::DebtorAddress.new(
161
+ country_code: 'CH',
162
+ address_line1: 'Mustergasse 123',
163
+ address_line2: '1234 Musterstadt'
164
+ )
165
+
166
+ sdd.add_transaction(
167
+ {
168
+ name: 'HANS TESTER',
169
+ iban: debtior_iban,
170
+ currency: 'CHF',
171
+ amount: '100.0',
172
+ remittance_information: 'According to invoice 4712',
173
+ reference: 'XYZ/2013-08-ABO/12345',
174
+ service_level: service_level,
175
+ local_instrument: local_instrument,
176
+ requested_date: requested_date,
177
+ instruction: 23,
178
+ debtor_address: sda,
179
+ structured_remittance_information: SPS::StructuredRemittanceInformation.new(
180
+ proprietary: 'ESR',
181
+ reference: '185744810000000000200800628'
182
+ )
183
+ }.merge(additional_fields)
184
+ )
185
+
186
+ sdd
187
+ end
188
+
189
+ let(:service_level) { 'CHTA' }
190
+ let(:local_instrument) { 'LSV+' }
191
+
192
+ let(:additional_fields) { {} }
193
+
194
+ let(:requested_date) { Date.today.next }
195
+
196
+ context 'as xml' do
197
+ subject do
198
+ direct_debit.to_xml(SPS::PAIN_008_001_02_CH_03)
199
+ end
200
+
201
+ it 'should have creditor identifier' do
202
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/GrpHdr/InitgPty/Id/OrgId/Othr/Id', direct_debit.account.creditor_identifier)
203
+ end
204
+
205
+ it 'should contain <PmtInfId>' do
206
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/PmtInfId', /#{message_id_regex}\/1/)
207
+ end
208
+
209
+ it 'should contain <ReqdColltnDt>' do
210
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/ReqdColltnDt', requested_date.iso8601)
211
+ end
212
+
213
+ it 'should contain <PmtMtd>' do
214
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/PmtMtd', 'DD')
215
+ end
216
+
217
+ it 'should not contain <BtchBookg>' do
218
+ expect(subject).not_to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/BtchBookg')
219
+ end
220
+
221
+ it 'should not contain <NbOfTxs>' do
222
+ expect(subject).not_to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/NbOfTxs')
223
+ end
224
+
225
+ it 'should not contain <CtrlSum>' do
226
+ expect(subject).not_to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/CtrlSum')
227
+ end
228
+
229
+ it 'should contain <Cdtr>' do
230
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/Cdtr/Nm', 'Muster AG')
231
+ end
232
+
233
+ it 'should contain <CdtrAcct>' do
234
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/CdtrAcct/Id/IBAN', 'CH7081232000001998736')
235
+ end
236
+
237
+ it 'should contain <CdtrAgt>' do
238
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/CdtrAgt/FinInstnId/ClrSysMmbId/MmbId', '81232')
239
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/CdtrAgt/FinInstnId/Othr/Id', '010001456')
240
+ end
241
+
242
+ it 'should not contain <ChrgBr>' do
243
+ expect(subject).not_to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/ChrgBr')
244
+ end
245
+
246
+ context 'when service_level is CHTA' do
247
+ it 'should contain <CdtrSchmeId>' do
248
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/CdtrSchmeId/Id/PrvtId/Othr/Id', direct_debit.account.creditor_identifier)
249
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/CdtrSchmeId/Id/PrvtId/Othr/SchmeNm/Prtry', 'CHLS')
250
+ end
251
+ end
252
+
253
+ context 'when service_level is CHDD' do
254
+ let(:service_level) { 'CHDD' }
255
+ let(:local_instrument) { 'DDCOR1' }
256
+
257
+ it 'should contain <CdtrSchmeId>' do
258
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/CdtrSchmeId/Id/PrvtId/Othr/Id', direct_debit.account.creditor_identifier)
259
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/CdtrSchmeId/Id/PrvtId/Othr/SchmeNm/Prtry', 'CHDD')
260
+ end
261
+ end
262
+
263
+ it 'should contain <EndToEndId>' do
264
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/DrctDbtTxInf[1]/PmtId/EndToEndId', 'XYZ/2013-08-ABO/12345')
265
+ end
266
+
267
+ it 'should contain <InstdAmt>' do
268
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/DrctDbtTxInf[1]/InstdAmt', '100.00')
269
+ end
270
+
271
+ it 'should contain <DbtrAgt>' do
272
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/DrctDbtTxInf[1]/DbtrAgt/FinInstnId/ClrSysMmbId/MmbId', '4835')
273
+ end
274
+
275
+ it 'should contain <Dbtr>' do
276
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/DrctDbtTxInf[1]/Dbtr/Nm', 'HANS TESTER')
277
+ end
278
+
279
+ it 'should contain <DbtrAcct>' do
280
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/DrctDbtTxInf[1]/DbtrAcct/Id/IBAN', 'CH9804835011062385295')
281
+ end
282
+
283
+ it 'should contain <Ustrd>' do
284
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/DrctDbtTxInf[1]/RmtInf/Ustrd', 'According to invoice 4712')
285
+ end
286
+
287
+ it 'should contain <Strd>' do
288
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/DrctDbtTxInf[1]/RmtInf/Strd/CdtrRefInf/Tp/CdOrPrtry/Prtry', 'ESR')
289
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/DrctDbtTxInf[1]/RmtInf/Strd/CdtrRefInf/Ref', '185744810000000000200800628')
290
+ end
291
+ end
292
+
293
+ context 'with service_level CHDD' do
294
+ let(:service_level) { 'CHDD' }
295
+
296
+ context 'with local_instrument DDCOR1' do
297
+ let(:local_instrument) { 'DDCOR1' }
298
+
299
+ it 'should validate against pain.008.001.02.ch.03' do
300
+ expect(direct_debit.to_xml(SPS::PAIN_008_001_02_CH_03)).to validate_against('pain.008.001.02.ch.03.xsd')
301
+ end
302
+ end
303
+
304
+ context 'with local_instrument DDB2B' do
305
+ let(:local_instrument) { 'DDB2B' }
306
+
307
+ it 'should validate against pain.008.001.02.ch.03' do
308
+ expect(direct_debit.to_xml(SPS::PAIN_008_001_02_CH_03)).to validate_against('pain.008.001.02.ch.03.xsd')
309
+ end
310
+ end
311
+ end
312
+
313
+ context 'with service_level CHTA' do
314
+ let(:service_level) { 'CHTA' }
315
+
316
+ context 'with local_instrument LSV+' do
317
+ let(:local_instrument) { 'LSV+' }
318
+
319
+ it 'should validate against pain.008.001.02.ch.03' do
320
+ expect(direct_debit.to_xml(SPS::PAIN_008_001_02_CH_03)).to validate_against('pain.008.001.02.ch.03.xsd')
321
+ end
322
+ end
323
+
324
+ context 'with local_instrument BDD' do
325
+ let(:local_instrument) { 'BDD' }
326
+
327
+ it 'should validate against pain.008.001.02.ch.03' do
328
+ expect(direct_debit.to_xml(SPS::PAIN_008_001_02_CH_03)).to validate_against('pain.008.001.02.ch.03.xsd')
329
+ end
330
+ end
331
+ end
332
+
333
+ context 'without structured_remittance_information' do
334
+ it 'should not be schema compatible' do
335
+ direct_debit.transactions.first.structured_remittance_information = nil
336
+
337
+ expect {
338
+ direct_debit.to_xml(SPS::PAIN_008_001_02_CH_03)
339
+ }.to raise_error(SPS::Error, "Incompatible with schema pain.008.001.02.ch.03!")
340
+ end
341
+ end
342
+ end
343
+
344
+ context 'without requested_date given' do
345
+ subject do
346
+ sdd = direct_debit
347
+
348
+ sdd.add_transaction(
349
+ name: 'Zahlemann & Söhne GbR',
350
+ iban: 'CH9804835011062385295',
351
+ amount: 39.99,
352
+ instruction: '12',
353
+ reference: 'XYZ/2013-08-ABO/12345',
354
+ remittance_information: 'Unsere Rechnung vom 10.08.2013',
355
+ structured_remittance_information: structured_remittance_information
356
+ )
357
+
358
+ sdd.add_transaction(
359
+ name: 'Meier & Schulze oHG',
360
+ iban: 'CH7081232000001998736',
361
+ amount: 750.00,
362
+ instruction: '34',
363
+ reference: 'XYZ/2013-08-ABO/6789',
364
+ remittance_information: 'Vielen Dank für Ihren Einkauf!',
365
+ structured_remittance_information: structured_remittance_information
366
+ )
367
+
368
+ sdd.to_xml
369
+ end
370
+
371
+ it 'should create valid XML file' do
372
+ expect(subject).to validate_against('pain.008.001.02.ch.03.xsd')
373
+ end
374
+
375
+ it 'should contain <ReqdColltnDt>' do
376
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf/ReqdColltnDt', Date.new(1999, 1, 1).iso8601)
377
+ end
378
+ end
379
+
380
+ context 'with different requested_date given' do
381
+ subject do
382
+ sdd = direct_debit
383
+
384
+ sdd.add_transaction(direct_debt_transaction.merge requested_date: Date.today + 1)
385
+ sdd.add_transaction(direct_debt_transaction.merge requested_date: Date.today + 2)
386
+ sdd.add_transaction(direct_debt_transaction.merge requested_date: Date.today + 2)
387
+
388
+ sdd.to_xml
389
+ end
390
+
391
+ it 'should contain two payment_informations with <ReqdColltnDt>' do
392
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf[1]/ReqdColltnDt', (Date.today + 1).iso8601)
393
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf[2]/ReqdColltnDt', (Date.today + 2).iso8601)
394
+
395
+ expect(subject).not_to have_xml('//Document/CstmrDrctDbtInitn/PmtInf[3]')
396
+ end
397
+
398
+ it 'should contain two payment_informations with different <PmtInfId>' do
399
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf[1]/PmtInfId', /#{message_id_regex}\/1/)
400
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf[2]/PmtInfId', /#{message_id_regex}\/2/)
401
+ end
402
+ end
403
+
404
+ context 'with different local_instrument given' do
405
+ subject do
406
+ sdd = direct_debit
407
+
408
+ sdd.add_transaction(direct_debt_transaction.merge local_instrument: 'LSV+')
409
+ sdd.add_transaction(direct_debt_transaction.merge local_instrument: 'BDD')
410
+
411
+ sdd
412
+ end
413
+
414
+ it 'should have errors' do
415
+ expect(subject.errors_on(:base).size).to eq(1)
416
+ end
417
+
418
+ it 'should raise error on XML generation' do
419
+ expect {
420
+ subject.to_xml
421
+ }.to raise_error(SPS::Error)
422
+ end
423
+ end
424
+
425
+ context 'with mismatching local_instrument given' do
426
+ subject do
427
+ sdd = direct_debit
428
+
429
+
430
+ sdd.add_transaction(direct_debt_transaction.merge(local_instrument: 'DDCOR1'))
431
+
432
+ sdd
433
+ end
434
+
435
+ it 'raises an ArgumentError' do
436
+ expect {
437
+ subject.add_transaction(direct_debt_transaction.merge(local_instrument: 'DDB2B'))
438
+ }.to raise_error(
439
+ ArgumentError,
440
+ 'Local instrument is not correct. Must be one of LSV+, BDD'
441
+ )
442
+ end
443
+ end
444
+
445
+ context 'with transactions containing different creditor_account' do
446
+ subject do
447
+ sdd = direct_debit
448
+
449
+ sdd.add_transaction(direct_debt_transaction)
450
+ sdd.add_transaction(
451
+ direct_debt_transaction.merge(
452
+ creditor_account: SPS::CreditorAccount.new(
453
+ name: 'Creditor Inc.',
454
+ iban: 'CH5604835012345678009',
455
+ creditor_identifier: 'ABC1W'
456
+ )
457
+ )
458
+ )
459
+
460
+ sdd.to_xml
461
+ end
462
+
463
+ it 'should contain two payment_informations with <Cdtr>' do
464
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf[1]/Cdtr/Nm', 'Gläubiger GmbH')
465
+ expect(subject).to have_xml('//Document/CstmrDrctDbtInitn/PmtInf[2]/Cdtr/Nm', 'Creditor Inc.')
466
+ end
467
+ end
468
+
469
+ context 'with large message identification' do
470
+ subject do
471
+ sct = direct_debit
472
+ sct.message_identification = 'A' * 35
473
+ sct.add_transaction(direct_debt_transaction.merge(instruction: '1234/ABC'))
474
+ sct
475
+ end
476
+
477
+ it 'should fail as the payment identification becomes too large' do
478
+ expect { subject.to_xml }.to raise_error(SPS::Error)
479
+ end
480
+ end
481
+ end
482
+ end
483
+ end