sps_king 0.6.1 → 0.8.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.
@@ -7,30 +7,53 @@ module SPS
7
7
  extend Converter
8
8
 
9
9
  attr_accessor :street_name,
10
+ :department,
11
+ :sub_department,
10
12
  :building_number,
13
+ :building_name,
14
+ :floor,
15
+ :post_box,
16
+ :room,
11
17
  :post_code,
12
18
  :town_name,
19
+ :town_location_name,
20
+ :district_name,
21
+ :country_subdivision,
13
22
  :country_code,
14
23
  :address_line1,
15
24
  :address_line2
16
25
 
17
- convert :street_name, to: :text
18
- convert :building_number, to: :text
19
- convert :post_code, to: :text
20
- convert :town_name, to: :text
21
- convert :country_code, to: :text
22
- convert :address_line1, to: :text
23
- convert :address_line2, to: :text
26
+ convert :street_name, to: :text
27
+ convert :department, to: :text
28
+ convert :sub_department, to: :text
29
+ convert :building_number, to: :text
30
+ convert :building_name, to: :text
31
+ convert :floor, to: :text
32
+ convert :post_box, to: :text
33
+ convert :room, to: :text
34
+ convert :post_code, to: :text
35
+ convert :town_name, to: :text
36
+ convert :town_location_name, to: :text
37
+ convert :district_name, to: :text
38
+ convert :country_subdivision, to: :text
39
+ convert :country_code, to: :text
40
+ convert :address_line1, to: :text
41
+ convert :address_line2, to: :text
24
42
 
25
43
  validates :street_name, length: { maximum: 70 }
26
44
  validates :building_number, length: { maximum: 16 }
45
+ validates :building_name, length: { maximum: 35 }
46
+ validates :floor, length: { maximum: 70 }
47
+ validates :post_box, length: { maximum: 16 }
48
+ validates :room, length: { maximum: 70 }
27
49
  validates :post_code, length: { maximum: 16 }
28
50
  validates :town_name, length: { maximum: 35 }
29
- validates :address_line1, length: { maximum: 70 }
30
- validates :address_line2, length: { maximum: 70 }
51
+ validates :country_subdivision, length: { maximum: 35 }
31
52
  validates :country_code,
32
53
  presence: true,
33
54
  format: { with: /\A[A-Z]{2}\z/ }
55
+ validates :address_line1, length: { maximum: 70 }
56
+ validates :address_line2, length: { maximum: 70 }
34
57
  # either town_name or address_line2 must be present
35
58
  validates :address_line2, presence: true, if: :town_name_blank?
36
59
  validates :town_name, presence: true, if: :address_line2_blank?
@@ -2,5 +2,20 @@
2
2
 
3
3
  module SPS
4
4
  class DebtorAccount < Account
5
+
6
+ extend Converter
7
+
8
+ attr_accessor :proprietary
9
+
10
+ ### Proprietary values
11
+ # NOA No Advice
12
+ # SIA Single Advice
13
+ # CND Collective Advice No Details
14
+ # CWD Collective Advice With Details
15
+ PROPRIETARY_VALUES = %w[NOA SIA CND CWD].freeze
16
+
17
+ validates :proprietary, inclusion: PROPRIETARY_VALUES, allow_nil: true
18
+
19
+ convert :proprietary, to: :text
5
20
  end
6
21
  end
@@ -7,7 +7,8 @@ module SPS
7
7
  self.transaction_class = CreditTransferTransaction
8
8
  self.xml_main_tag = 'CstmrCdtTrfInitn'
9
9
  self.known_schemas = [
10
- PAIN_001_001_03_CH_02
10
+ PAIN_001_001_03_CH_02,
11
+ PAIN_001_001_09_CH_03,
11
12
  ]
12
13
 
13
14
  private
@@ -23,7 +24,7 @@ module SPS
23
24
  }
24
25
  end
25
26
 
26
- def build_payment_informations(builder)
27
+ def build_payment_informations(builder, schema_name)
27
28
  # Build a PmtInf block for every group of transactions
28
29
  grouped_transactions.each do |group, transactions|
29
30
  # All transactions with the same requested_date are placed into the same PmtInf block
@@ -45,7 +46,13 @@ module SPS
45
46
  end
46
47
  end
47
48
  end
48
- builder.ReqdExctnDt(group[:requested_date].iso8601)
49
+ if schema_name == PAIN_001_001_09_CH_03
50
+ builder.ReqdExctnDt do
51
+ builder.Dt(group[:requested_date].iso8601)
52
+ end
53
+ else
54
+ builder.ReqdExctnDt(group[:requested_date].iso8601)
55
+ end
49
56
  builder.Dbtr do
50
57
  builder.Nm(account.name)
51
58
  end
@@ -53,10 +60,19 @@ module SPS
53
60
  builder.Id do
54
61
  builder.IBAN(account.iban)
55
62
  end
63
+ if account.proprietary.present?
64
+ builder.Tp do
65
+ builder.Prtry(account.proprietary)
66
+ end
67
+ end
56
68
  end
57
69
  builder.DbtrAgt do
58
70
  builder.FinInstnId do
59
- builder.BIC(account.bic)
71
+ if schema_name == PAIN_001_001_09_CH_03
72
+ builder.BICFI(account.bic)
73
+ else
74
+ builder.BIC(account.bic)
75
+ end
60
76
  end
61
77
  end
62
78
  if group[:charge_bearer]
@@ -64,13 +80,13 @@ module SPS
64
80
  end
65
81
 
66
82
  transactions.each do |transaction|
67
- build_transaction(builder, transaction)
83
+ build_transaction(builder, transaction, schema_name)
68
84
  end
69
85
  end
70
86
  end
71
87
  end
72
88
 
73
- def build_transaction(builder, transaction)
89
+ def build_transaction(builder, transaction, schema_name)
74
90
  builder.CdtTrfTxInf do
75
91
  builder.PmtId do
76
92
  if transaction.instruction.present?
@@ -84,7 +100,11 @@ module SPS
84
100
  if transaction.bic
85
101
  builder.CdtrAgt do
86
102
  builder.FinInstnId do
87
- builder.BIC(transaction.bic)
103
+ if schema_name == PAIN_001_001_09_CH_03
104
+ builder.BICFI(transaction.bic)
105
+ else
106
+ builder.BIC(transaction.bic)
107
+ end
88
108
  end
89
109
  end
90
110
  end
@@ -94,11 +114,19 @@ module SPS
94
114
  builder.PstlAdr do
95
115
  # Only set the fields that are actually provided.
96
116
  # StrtNm, BldgNb, PstCd, TwnNm provide a structured address
97
- # separated into its individual fields.
117
+ # separated into its individual fields. More detailed fields are possible
98
118
  # AdrLine provides the address in free format text.
99
119
  # Both are currently allowed and the actual preference depends on the bank.
100
120
  # Also the fields that are required legally may vary depending on the country
101
121
  # or change over time.
122
+ if transaction.creditor_address.department
123
+ builder.Dept transaction.creditor_address.department # v1.9
124
+ end
125
+
126
+ if transaction.creditor_address.sub_department
127
+ builder.SubDept transaction.creditor_address.sub_department # v1.9
128
+ end
129
+
102
130
  if transaction.creditor_address.street_name
103
131
  builder.StrtNm transaction.creditor_address.street_name
104
132
  end
@@ -107,6 +135,22 @@ module SPS
107
135
  builder.BldgNb transaction.creditor_address.building_number
108
136
  end
109
137
 
138
+ if transaction.creditor_address.building_name
139
+ builder.BldgNm transaction.creditor_address.building_name # v1.9
140
+ end
141
+
142
+ if transaction.creditor_address.floor
143
+ builder.Flr transaction.creditor_address.floor # v1.9
144
+ end
145
+
146
+ if transaction.creditor_address.post_box
147
+ builder.PstBx transaction.creditor_address.post_box # v1.9
148
+ end
149
+
150
+ if transaction.creditor_address.room
151
+ builder.Room transaction.creditor_address.room # v1.9
152
+ end
153
+
110
154
  if transaction.creditor_address.post_code
111
155
  builder.PstCd transaction.creditor_address.post_code
112
156
  end
@@ -115,6 +159,18 @@ module SPS
115
159
  builder.TwnNm transaction.creditor_address.town_name
116
160
  end
117
161
 
162
+ if transaction.creditor_address.town_location_name
163
+ builder.TwnLctnNm transaction.creditor_address.town_location_name # v1.9
164
+ end
165
+
166
+ if transaction.creditor_address.district_name
167
+ builder.DstrctNm transaction.creditor_address.district_name # v1.9
168
+ end
169
+
170
+ if transaction.creditor_address.country_subdivision
171
+ builder.CtrySubDvsn transaction.creditor_address.country_subdivision # v1.9
172
+ end
173
+
118
174
  if transaction.creditor_address.country_code
119
175
  builder.Ctry transaction.creditor_address.country_code
120
176
  end
@@ -27,7 +27,7 @@ module SPS
27
27
  requested_date: transaction.requested_date,
28
28
  service_level: transaction.service_level,
29
29
  local_instrument: transaction.local_instrument,
30
- account: transaction.creditor_account || account
30
+ account: transaction.creditor_account || account,
31
31
  }
32
32
  end
33
33
 
@@ -46,7 +46,7 @@ module SPS
46
46
  return iban.to_s[4..8].sub(/^0*/, '')
47
47
  end
48
48
 
49
- def build_payment_informations(builder)
49
+ def build_payment_informations(builder, _schema_name)
50
50
  # Build a PmtInf block for every group of transactions
51
51
  grouped_transactions.each do |group, transactions|
52
52
  builder.PmtInf do
@@ -166,10 +166,10 @@ module SPS
166
166
  builder.CdtrRefInf do
167
167
  builder.Tp do
168
168
  builder.CdOrPrtry do
169
- builder.Prtry(transaction.structured_remittance_information.proprietary)
169
+ builder.Prtry(transaction.structured_remittance_information&.proprietary)
170
170
  end
171
171
  end
172
- builder.Ref(transaction.structured_remittance_information.reference)
172
+ builder.Ref(transaction.structured_remittance_information&.reference)
173
173
  end
174
174
  end
175
175
  end
@@ -1,9 +1,10 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  module SPS
4
4
 
5
5
  PAIN_008_001_02_CH_03 = 'pain.008.001.02.ch.03'
6
6
  PAIN_001_001_03_CH_02 = 'pain.001.001.03.ch.02'
7
+ PAIN_001_001_09_CH_03 = 'pain.001.001.09.ch.03'
7
8
 
8
9
  class Message
9
10
 
@@ -38,13 +39,13 @@ module SPS
38
39
  # @return [String] xml
39
40
  def to_xml(schema_name = self.known_schemas.first, encoding = 'UTF-8')
40
41
  raise SPS::Error.new(errors.full_messages.join("\n")) unless valid?
41
- raise SPS::Error.new("Incompatible with schema #{schema_name}!") unless schema_compatible?(schema_name)
42
+ # raise SPS::Error.new("Incompatible with schema #{schema_name}!") unless schema_compatible?(schema_name)
42
43
 
43
44
  builder = Nokogiri::XML::Builder.new(encoding: encoding) do |builder|
44
45
  builder.Document(xml_schema(schema_name)) do
45
46
  builder.__send__(xml_main_tag) do
46
47
  build_group_header(builder)
47
- build_payment_informations(builder)
48
+ build_payment_informations(builder, schema_name)
48
49
  end
49
50
  end
50
51
  end
@@ -61,7 +62,7 @@ module SPS
61
62
  raise ArgumentError.new("Schema #{schema_name} is unknown!") unless self.known_schemas.include?(schema_name)
62
63
 
63
64
  case schema_name
64
- when PAIN_001_001_03_CH_02
65
+ when PAIN_001_001_03_CH_02, PAIN_001_001_09_CH_03
65
66
  transactions.all? { |t| t.schema_compatible?(schema_name) }
66
67
  when PAIN_008_001_02_CH_03
67
68
  transactions.all? { |t| t.schema_compatible?(schema_name) } &&
@@ -85,7 +86,8 @@ module SPS
85
86
  end
86
87
 
87
88
  # Set creation date time for the message
88
- # p.s. Rabobank in the Netherlands only accepts the more restricted format [0-9]{4}[-][0-9]{2,2}[-][0-9]{2,2}[T][0-9]{2,2}[:][0-9]{2,2}[:][0-9]{2,2}
89
+ # p.s. Rabobank in the Netherlands only accepts the more restricted format
90
+ # [0-9]{4}[-][0-9]{2,2}[-][0-9]{2,2}[T][0-9]{2,2}[:][0-9]{2,2}[:][0-9]{2,2}
89
91
  def creation_date_time=(value)
90
92
  raise ArgumentError.new('creation_date_time must be a string!') unless value.is_a?(String)
91
93
 
@@ -104,7 +106,7 @@ module SPS
104
106
  # Identified based upon the reference of the transaction
105
107
  def batch_id(transaction_reference)
106
108
  grouped_transactions.each do |group, transactions|
107
- if transactions.select { |transaction| transaction.reference == transaction_reference }.any?
109
+ if transactions.any? { |transaction| transaction.reference == transaction_reference }
108
110
  return payment_information_identification(group)
109
111
  end
110
112
  end
@@ -118,11 +120,20 @@ module SPS
118
120
 
119
121
  # @return {Hash<Symbol=>String>} xml schema information used in output xml
120
122
  def xml_schema(schema_name)
121
- {
122
- :xmlns => "http://www.six-interbank-clearing.com/de/#{schema_name}.xsd",
123
- :'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
124
- :'xsi:schemaLocation' => "http://www.six-interbank-clearing.com/de/#{schema_name}.xsd #{schema_name}.xsd"
125
- }
123
+ data = {}
124
+ if schema_name.include?('pain.001.001.09') # v1.9 logic
125
+ data[:xmlns] = 'urn:iso:std:iso:20022:tech:xsd:pain.001.001.09'
126
+ data[:'xmlns:xsi'] = 'http://www.w3.org/2001/XMLSchema-instance'
127
+ data[:'xsi:schemaLocation'] = 'urn:iso:std:iso:20022:tech:xsd:pain.001.001.09 ' \
128
+ 'pain.001.001.09.ch.03.xsd'
129
+ else # old SIX logic
130
+ data[:xmlns] = "http://www.six-interbank-clearing.com/de/#{schema_name}.xsd"
131
+ data[:'xmlns:xsi'] = 'http://www.w3.org/2001/XMLSchema-instance'
132
+ data[:'xsi:schemaLocation'] = 'http://www.six-interbank-clearing.com/de' \
133
+ "/#{schema_name}.xsd #{schema_name}.xsd"
134
+ end
135
+
136
+ data
126
137
  end
127
138
 
128
139
  def build_group_header(builder)
@@ -157,11 +168,13 @@ module SPS
157
168
  def validate_final_document!(document, schema_name)
158
169
  xsd = Nokogiri::XML::Schema(
159
170
  File.read(
160
- File.expand_path("../../../lib/schema/#{schema_name}.xsd", __FILE__)
161
- )
171
+ File.expand_path("../../../lib/schema/#{schema_name}.xsd", __FILE__),
172
+ ),
162
173
  )
163
- errors = xsd.validate(document).map { |error| error.message }
164
- raise SPS::Error.new("Incompatible with schema #{schema_name}: #{errors.join(', ')}") if errors.any?
174
+ errors = xsd.validate(document).map(&:message)
175
+ if errors.any?
176
+ raise SPS::Error.new("Incompatible with schema #{schema_name}: #{errors.join(', ')}")
177
+ end
165
178
  end
166
179
 
167
180
  end
@@ -4,6 +4,7 @@ module SPS
4
4
  class CreditTransferTransaction < Transaction
5
5
 
6
6
  attr_accessor :service_level,
7
+ :local_instrument,
7
8
  :creditor_address,
8
9
  :category_purpose,
9
10
  :charge_bearer
@@ -18,8 +19,8 @@ module SPS
18
19
 
19
20
  def schema_compatible?(schema_name)
20
21
  case schema_name
21
- when PAIN_001_001_03_CH_02
22
- !self.bic.nil?
22
+ when PAIN_001_001_03_CH_02, PAIN_001_001_09_CH_03
23
+ self.bic != nil
23
24
  end
24
25
  end
25
26
 
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SPS
2
4
 
3
- VERSION = '0.6.1'
5
+ VERSION = '0.8.0'
4
6
 
5
7
  end
@@ -61,10 +61,16 @@ describe SPS::Address do
61
61
  let(:subject_too_long) do
62
62
  described_class.new(
63
63
  country_code:,
64
- street_name: 'X' * 71,
65
- building_number: 'X' * 17,
66
- post_code: 'X' * 17,
67
- town_name: 'X' * 36,
64
+ street_name: 'X' * 71,
65
+ building_number: 'X' * 17,
66
+ building_name: 'X' * 36,
67
+ floor: 'X' * 71,
68
+ post_box: 'X' * 17,
69
+ room: 'X' * 71,
70
+ post_code: 'X' * 17,
71
+ town_name: 'X' * 36,
72
+ country_subdivision: 'X' * 36,
73
+ country_code: 'CHU',
68
74
  )
69
75
  end
70
76
 
@@ -8,7 +8,8 @@ describe SPS::DebtorAccount do
8
8
  SPS::DebtorAccount.new(
9
9
  name: 'Gläubiger GmbH',
10
10
  bic: 'BANKDEFFXXX',
11
- iban: 'DE87200500001234567890'
11
+ iban: 'DE87200500001234567890',
12
+ proprietary: 'CWD'
12
13
  )
13
14
  ).to be_valid
14
15
  end
@@ -1,4 +1,4 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  require 'spec_helper'
4
4
 
@@ -78,6 +78,12 @@ describe SPS::CreditTransfer do
78
78
  subject.to_xml(SPS::PAIN_001_001_03_CH_02)
79
79
  ).to validate_against('pain.001.001.03.ch.02.xsd')
80
80
  end
81
+
82
+ it 'should validate against pain.001.001.09.ch.03' do
83
+ expect(
84
+ subject.to_xml(SPS::PAIN_001_001_09_CH_03)
85
+ ).to validate_against('pain.001.001.09.ch.03.xsd')
86
+ end
81
87
  end
82
88
 
83
89
  context 'setting creditor address with structured fields' do
@@ -136,6 +142,10 @@ describe SPS::CreditTransfer do
136
142
  it 'should validate against pain.001.001.03.ch.02' do
137
143
  expect(subject.to_xml('pain.001.001.03.ch.02')).to validate_against('pain.001.001.03.ch.02.xsd')
138
144
  end
145
+
146
+ it 'should validate against pain.001.001.09.ch.03' do
147
+ expect(subject.to_xml('pain.001.001.09.ch.03')).to validate_against('pain.001.001.09.ch.03.xsd')
148
+ end
139
149
  end
140
150
 
141
151
  context 'without requested_date given' do
@@ -535,5 +545,38 @@ describe SPS::CreditTransfer do
535
545
  expect(subject).to include('ISO-8859-8')
536
546
  end
537
547
  end
548
+
549
+ context 'with schema v1.9' do
550
+ let(:credit_transfer) do
551
+ sct = SPS::CreditTransfer.new(
552
+ name: 'Schuldner GmbH',
553
+ iban: 'CH5481230000001998736',
554
+ bic: 'RAIFCH22',
555
+ )
556
+ creditor_address = SPS::CreditorAddress.new(
557
+ country_code: 'CH',
558
+ street_name: 'Mustergasse',
559
+ building_number: '123',
560
+ post_code: '1234',
561
+ town_name: 'Musterstadt'
562
+ )
563
+
564
+ sct.add_transaction(
565
+ name: 'Contoso AG',
566
+ bic: 'CRESCHZZ80A',
567
+ iban: 'CH9300762011623852957',
568
+ amount: 102.50,
569
+ reference: 'XYZ-1234/123',
570
+ remittance_information: 'Rechnung vom 22.08.2013',
571
+ creditor_address: creditor_address,
572
+ )
573
+ sct
574
+ end
575
+
576
+ it 'should be valid against v1.9' do
577
+ expect(credit_transfer.to_xml(SPS::PAIN_001_001_09_CH_03))
578
+ .to validate_against('pain.001.001.09.ch.03.xsd')
579
+ end
580
+ end
538
581
  end
539
582
  end
@@ -384,9 +384,9 @@ describe SPS::DirectDebit do
384
384
  it 'should not be schema compatible' do
385
385
  direct_debit.transactions.first.structured_remittance_information = nil
386
386
 
387
- expect {
387
+ expect do
388
388
  direct_debit.to_xml(SPS::PAIN_008_001_02_CH_03)
389
- }.to raise_error(SPS::Error, "Incompatible with schema pain.008.001.02.ch.03!")
389
+ end.to raise_error(SPS::Error)
390
390
  end
391
391
  end
392
392
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sps_king
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tobias Schoknecht
@@ -171,6 +171,7 @@ files:
171
171
  - gemfiles/Gemfile-activemodel-7.2.x
172
172
  - gemfiles/Gemfile-activemodel-8.0.x
173
173
  - lib/schema/pain.001.001.03.ch.02.xsd
174
+ - lib/schema/pain.001.001.09.ch.03.xsd
174
175
  - lib/schema/pain.008.001.02.ch.03.xsd
175
176
  - lib/sps_king.rb
176
177
  - lib/sps_king/account.rb
@@ -230,7 +231,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
230
231
  - !ruby/object:Gem::Version
231
232
  version: '0'
232
233
  requirements: []
233
- rubygems_version: 4.0.3
234
+ rubygems_version: 4.0.6
234
235
  specification_version: 4
235
236
  summary: Ruby gem for creating SPS XML files
236
237
  test_files: