sps_king 0.4.0 → 0.6.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.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +3 -3
- data/CHANGELOG.md +46 -23
- data/README.md +29 -13
- data/cliff.toml +3 -3
- data/gemfiles/{Gemfile-activemodel-6.1.x → Gemfile-activemodel-7.2.x} +1 -1
- data/gemfiles/{Gemfile-activemodel-7.0.x → Gemfile-activemodel-8.0.x} +1 -1
- data/lib/sps_king/account/address.rb +55 -0
- data/lib/sps_king/account/creditor_account.rb +3 -0
- data/lib/sps_king/account/creditor_address.rb +2 -34
- data/lib/sps_king/account/debtor_account.rb +1 -0
- data/lib/sps_king/account/debtor_address.rb +2 -31
- data/lib/sps_king/account.rb +3 -0
- data/lib/sps_king/converter.rb +14 -10
- data/lib/sps_king/message/credit_transfer.rb +120 -102
- data/lib/sps_king/message/direct_debit.rb +121 -118
- data/lib/sps_king/message.rb +54 -48
- data/lib/sps_king/structured_remittance_information.rb +3 -1
- data/lib/sps_king/transaction/credit_transfer_transaction.rb +3 -0
- data/lib/sps_king/transaction/direct_debit_transaction.rb +9 -6
- data/lib/sps_king/transaction.rb +10 -7
- data/lib/sps_king/validator.rb +9 -0
- data/lib/sps_king/version.rb +3 -1
- data/lib/sps_king.rb +1 -0
- data/spec/lib/sps_king/account/address_spec.rb +85 -0
- data/spec/lib/sps_king/account/creditor_account_spec.rb +14 -13
- data/spec/lib/sps_king/account/debtor_account_spec.rb +1 -0
- data/spec/lib/sps_king/account_spec.rb +12 -2
- data/spec/lib/sps_king/converter_spec.rb +25 -5
- data/spec/lib/sps_king/message/credit_transfer_spec.rb +205 -37
- data/spec/lib/sps_king/message/direct_debit_spec.rb +218 -102
- data/spec/lib/sps_king/message_spec.rb +23 -8
- data/spec/lib/sps_king/structured_remittance_information_spec.rb +8 -3
- data/spec/lib/sps_king/transaction/credit_transfer_transaction_spec.rb +14 -2
- data/spec/lib/sps_king/transaction/direct_debit_transaction_spec.rb +8 -7
- data/spec/lib/sps_king/transaction_spec.rb +62 -27
- data/spec/lib/sps_king/validator_spec.rb +38 -9
- data/spec/spec_helper.rb +8 -4
- data/spec/support/active_model.rb +3 -1
- data/spec/support/factories.rb +11 -10
- data/spec/support/validations.rb +2 -0
- data/sps_king.gemspec +1 -0
- metadata +8 -12
- data/spec/lib/sps_king/account/creditor_address_spec.rb +0 -14
- data/spec/lib/sps_king/account/debtor_address_spec.rb +0 -14
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module SPS
|
4
4
|
class CreditTransfer < Message
|
5
|
+
|
5
6
|
self.account_class = DebtorAccount
|
6
7
|
self.transaction_class = CreditTransferTransaction
|
7
8
|
self.xml_main_tag = 'CstmrCdtTrfInitn'
|
@@ -9,135 +10,152 @@ module SPS
|
|
9
10
|
PAIN_001_001_03_CH_02
|
10
11
|
]
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
13
|
+
private
|
14
|
+
|
15
|
+
# Find groups of transactions which share the same values of some attributes
|
16
|
+
def transaction_group(transaction)
|
17
|
+
{
|
18
|
+
requested_date: transaction.requested_date,
|
19
|
+
batch_booking: transaction.batch_booking,
|
20
|
+
service_level: transaction.service_level,
|
21
|
+
category_purpose: transaction.category_purpose,
|
22
|
+
charge_bearer: transaction.charge_bearer,
|
23
|
+
}
|
24
|
+
end
|
23
25
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
26
|
+
def build_payment_informations(builder)
|
27
|
+
# Build a PmtInf block for every group of transactions
|
28
|
+
grouped_transactions.each do |group, transactions|
|
29
|
+
# All transactions with the same requested_date are placed into the same PmtInf block
|
30
|
+
builder.PmtInf do
|
31
|
+
builder.PmtInfId(payment_information_identification(group))
|
32
|
+
builder.PmtMtd('TRF')
|
33
|
+
builder.BtchBookg(group[:batch_booking])
|
34
|
+
builder.NbOfTxs(transactions.length)
|
35
|
+
builder.CtrlSum('%.2f' % amount_total(transactions))
|
36
|
+
builder.PmtTpInf do
|
37
|
+
if group[:service_level]
|
38
|
+
builder.SvcLvl do
|
39
|
+
builder.Cd(group[:service_level])
|
40
|
+
end
|
38
41
|
end
|
42
|
+
if group[:category_purpose]
|
43
|
+
builder.CtgyPurp do
|
44
|
+
builder.Cd(group[:category_purpose])
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
builder.ReqdExctnDt(group[:requested_date].iso8601)
|
49
|
+
builder.Dbtr do
|
50
|
+
builder.Nm(account.name)
|
39
51
|
end
|
40
|
-
|
41
|
-
builder.
|
42
|
-
builder.
|
52
|
+
builder.DbtrAcct do
|
53
|
+
builder.Id do
|
54
|
+
builder.IBAN(account.iban)
|
43
55
|
end
|
44
56
|
end
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
end
|
50
|
-
builder.DbtrAcct do
|
51
|
-
builder.Id do
|
52
|
-
builder.IBAN(account.iban)
|
57
|
+
builder.DbtrAgt do
|
58
|
+
builder.FinInstnId do
|
59
|
+
builder.BIC(account.bic)
|
60
|
+
end
|
53
61
|
end
|
54
|
-
|
55
|
-
|
56
|
-
builder.FinInstnId do
|
57
|
-
builder.BIC(account.bic)
|
62
|
+
if group[:charge_bearer]
|
63
|
+
builder.ChrgBr(group[:charge_bearer])
|
58
64
|
end
|
59
|
-
end
|
60
|
-
if group[:charge_bearer]
|
61
|
-
builder.ChrgBr(group[:charge_bearer])
|
62
|
-
end
|
63
65
|
|
64
|
-
|
65
|
-
|
66
|
+
transactions.each do |transaction|
|
67
|
+
build_transaction(builder, transaction)
|
68
|
+
end
|
66
69
|
end
|
67
70
|
end
|
68
71
|
end
|
69
|
-
end
|
70
72
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
end
|
77
|
-
builder.EndToEndId(transaction.reference)
|
78
|
-
end
|
79
|
-
builder.Amt do
|
80
|
-
builder.InstdAmt('%.2f' % transaction.amount, Ccy: transaction.currency)
|
81
|
-
end
|
82
|
-
if transaction.bic
|
83
|
-
builder.CdtrAgt do
|
84
|
-
builder.FinInstnId do
|
85
|
-
builder.BIC(transaction.bic)
|
73
|
+
def build_transaction(builder, transaction)
|
74
|
+
builder.CdtTrfTxInf do
|
75
|
+
builder.PmtId do
|
76
|
+
if transaction.instruction.present?
|
77
|
+
builder.InstrId(transaction.instruction)
|
86
78
|
end
|
79
|
+
builder.EndToEndId(transaction.reference)
|
87
80
|
end
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
if transaction.
|
92
|
-
builder.
|
93
|
-
|
94
|
-
|
95
|
-
# separated into its individual fields.
|
96
|
-
# AdrLine provides the address in free format text.
|
97
|
-
# Both are currently allowed and the actual preference depends on the bank.
|
98
|
-
# Also the fields that are required legally may vary depending on the country
|
99
|
-
# or change over time.
|
100
|
-
if transaction.creditor_address.street_name
|
101
|
-
builder.StrtNm transaction.creditor_address.street_name
|
81
|
+
builder.Amt do
|
82
|
+
builder.InstdAmt('%.2f' % transaction.amount, Ccy: transaction.currency)
|
83
|
+
end
|
84
|
+
if transaction.bic
|
85
|
+
builder.CdtrAgt do
|
86
|
+
builder.FinInstnId do
|
87
|
+
builder.BIC(transaction.bic)
|
102
88
|
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
builder.Cdtr do
|
92
|
+
builder.Nm(transaction.name)
|
93
|
+
if transaction.creditor_address
|
94
|
+
builder.PstlAdr do
|
95
|
+
# Only set the fields that are actually provided.
|
96
|
+
# StrtNm, BldgNb, PstCd, TwnNm provide a structured address
|
97
|
+
# separated into its individual fields.
|
98
|
+
# AdrLine provides the address in free format text.
|
99
|
+
# Both are currently allowed and the actual preference depends on the bank.
|
100
|
+
# Also the fields that are required legally may vary depending on the country
|
101
|
+
# or change over time.
|
102
|
+
if transaction.creditor_address.street_name
|
103
|
+
builder.StrtNm transaction.creditor_address.street_name
|
104
|
+
end
|
103
105
|
|
104
|
-
|
105
|
-
|
106
|
-
|
106
|
+
if transaction.creditor_address.building_number
|
107
|
+
builder.BldgNb transaction.creditor_address.building_number
|
108
|
+
end
|
107
109
|
|
108
|
-
|
109
|
-
|
110
|
-
|
110
|
+
if transaction.creditor_address.post_code
|
111
|
+
builder.PstCd transaction.creditor_address.post_code
|
112
|
+
end
|
111
113
|
|
112
|
-
|
113
|
-
|
114
|
-
|
114
|
+
if transaction.creditor_address.town_name
|
115
|
+
builder.TwnNm transaction.creditor_address.town_name
|
116
|
+
end
|
115
117
|
|
116
|
-
|
117
|
-
|
118
|
-
|
118
|
+
if transaction.creditor_address.country_code
|
119
|
+
builder.Ctry transaction.creditor_address.country_code
|
120
|
+
end
|
119
121
|
|
120
|
-
|
121
|
-
|
122
|
-
|
122
|
+
if transaction.creditor_address.address_line1
|
123
|
+
builder.AdrLine transaction.creditor_address.address_line1
|
124
|
+
end
|
123
125
|
|
124
|
-
|
125
|
-
|
126
|
+
if transaction.creditor_address.address_line2
|
127
|
+
builder.AdrLine transaction.creditor_address.address_line2
|
128
|
+
end
|
126
129
|
end
|
127
130
|
end
|
128
131
|
end
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
132
|
+
builder.CdtrAcct do
|
133
|
+
builder.Id do
|
134
|
+
builder.IBAN(transaction.iban)
|
135
|
+
end
|
133
136
|
end
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
137
|
+
if transaction.remittance_information || transaction.structured_remittance_information
|
138
|
+
builder.RmtInf do
|
139
|
+
if transaction.remittance_information
|
140
|
+
builder.Ustrd(transaction.remittance_information)
|
141
|
+
end
|
142
|
+
|
143
|
+
if transaction.structured_remittance_information
|
144
|
+
builder.Strd do
|
145
|
+
builder.CdtrRefInf do
|
146
|
+
builder.Tp do
|
147
|
+
builder.CdOrPrtry do
|
148
|
+
builder.Prtry(transaction.structured_remittance_information.proprietary)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
builder.Ref(transaction.structured_remittance_information.reference)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
138
156
|
end
|
139
157
|
end
|
140
158
|
end
|
141
|
-
|
159
|
+
|
142
160
|
end
|
143
161
|
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module SPS
|
4
4
|
class DirectDebit < Message
|
5
|
+
|
5
6
|
self.account_class = CreditorAccount
|
6
7
|
self.transaction_class = DirectDebitTransaction
|
7
8
|
self.xml_main_tag = 'CstmrDrctDbtInitn'
|
@@ -18,160 +19,162 @@ module SPS
|
|
18
19
|
end
|
19
20
|
end
|
20
21
|
|
21
|
-
|
22
|
-
# Find groups of transactions which share the same values of some attributes
|
23
|
-
def transaction_group(transaction)
|
24
|
-
{
|
25
|
-
requested_date: transaction.requested_date,
|
26
|
-
service_level: transaction.service_level,
|
27
|
-
local_instrument: transaction.local_instrument,
|
28
|
-
account: transaction.creditor_account || account
|
29
|
-
}
|
30
|
-
end
|
22
|
+
private
|
31
23
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
24
|
+
# Find groups of transactions which share the same values of some attributes
|
25
|
+
def transaction_group(transaction)
|
26
|
+
{
|
27
|
+
requested_date: transaction.requested_date,
|
28
|
+
service_level: transaction.service_level,
|
29
|
+
local_instrument: transaction.local_instrument,
|
30
|
+
account: transaction.creditor_account || account
|
31
|
+
}
|
38
32
|
end
|
39
|
-
end
|
40
33
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
34
|
+
def creditor_scheme_name(service_level)
|
35
|
+
case service_level
|
36
|
+
when 'CHDD' # PAIN_008_001_02_CH_03 only
|
37
|
+
return 'CHDD'
|
38
|
+
when 'CHTA' # PAIN_008_001_02_CH_03 only
|
39
|
+
return 'CHLS'
|
40
|
+
end
|
41
|
+
end
|
46
42
|
|
47
|
-
|
48
|
-
#
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
builder.
|
58
|
-
|
43
|
+
# For IBANs from Switzerland or Liechtenstein the clearing system member id can be retrieved
|
44
|
+
# as the 5th to 9th digit of the IBAN, which is the local bank code.
|
45
|
+
def clearing_system_member_id_from_iban(iban)
|
46
|
+
return iban.to_s[4..8].sub(/^0*/, '')
|
47
|
+
end
|
48
|
+
|
49
|
+
def build_payment_informations(builder)
|
50
|
+
# Build a PmtInf block for every group of transactions
|
51
|
+
grouped_transactions.each do |group, transactions|
|
52
|
+
builder.PmtInf do
|
53
|
+
builder.PmtInfId(payment_information_identification(group))
|
54
|
+
builder.PmtMtd('DD')
|
55
|
+
builder.PmtTpInf do
|
56
|
+
builder.SvcLvl do
|
57
|
+
builder.Prtry(group[:service_level])
|
58
|
+
end
|
59
|
+
builder.LclInstrm do
|
60
|
+
builder.Prtry(group[:local_instrument])
|
61
|
+
end
|
59
62
|
end
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
builder.Nm(group[:account].name)
|
64
|
-
end
|
65
|
-
builder.CdtrAcct do
|
66
|
-
builder.Id do
|
67
|
-
builder.IBAN(group[:account].iban)
|
63
|
+
builder.ReqdColltnDt(group[:requested_date].iso8601)
|
64
|
+
builder.Cdtr do
|
65
|
+
builder.Nm(group[:account].name)
|
68
66
|
end
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
builder.ClrSysMmbId do
|
73
|
-
builder.MmbId(clearing_system_member_id_from_iban(group[:account].iban))
|
67
|
+
builder.CdtrAcct do
|
68
|
+
builder.Id do
|
69
|
+
builder.IBAN(group[:account].iban)
|
74
70
|
end
|
75
|
-
|
76
|
-
|
77
|
-
|
71
|
+
end
|
72
|
+
builder.CdtrAgt do
|
73
|
+
builder.FinInstnId do
|
74
|
+
builder.ClrSysMmbId do
|
75
|
+
builder.MmbId(clearing_system_member_id_from_iban(group[:account].iban))
|
76
|
+
end
|
77
|
+
if group[:account].isr_participant_number
|
78
|
+
builder.Othr do
|
79
|
+
builder.Id(group[:account].isr_participant_number)
|
80
|
+
end
|
78
81
|
end
|
79
82
|
end
|
80
83
|
end
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
84
|
+
builder.CdtrSchmeId do
|
85
|
+
builder.Id do
|
86
|
+
builder.PrvtId do
|
87
|
+
builder.Othr do
|
88
|
+
builder.Id(group[:account].creditor_identifier)
|
89
|
+
builder.SchmeNm do
|
90
|
+
builder.Prtry(creditor_scheme_name(group[:service_level]))
|
91
|
+
end
|
89
92
|
end
|
90
93
|
end
|
91
94
|
end
|
92
95
|
end
|
93
|
-
end
|
94
96
|
|
95
|
-
|
96
|
-
|
97
|
+
transactions.each do |transaction|
|
98
|
+
build_transaction(builder, transaction)
|
99
|
+
end
|
97
100
|
end
|
98
101
|
end
|
99
102
|
end
|
100
|
-
end
|
101
103
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
end
|
108
|
-
builder.InstdAmt('%.2f' % transaction.amount, Ccy: transaction.currency)
|
109
|
-
builder.DbtrAgt do
|
110
|
-
builder.FinInstnId do
|
111
|
-
builder.ClrSysMmbId do
|
112
|
-
builder.MmbId(clearing_system_member_id_from_iban(transaction.iban))
|
113
|
-
end
|
104
|
+
def build_transaction(builder, transaction)
|
105
|
+
builder.DrctDbtTxInf do
|
106
|
+
builder.PmtId do
|
107
|
+
builder.InstrId(transaction.instruction)
|
108
|
+
builder.EndToEndId(transaction.reference)
|
114
109
|
end
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
# Only set the fields that are actually provided.
|
121
|
-
# StrtNm, BldgNb, PstCd, TwnNm provide a structured address
|
122
|
-
# separated into its individual fields.
|
123
|
-
# AdrLine provides the address in free format text.
|
124
|
-
# Both are currently allowed and the actual preference depends on the bank.
|
125
|
-
# Also the fields that are required legally may vary depending on the country
|
126
|
-
# or change over time.
|
127
|
-
if transaction.debtor_address.street_name
|
128
|
-
builder.StrtNm transaction.debtor_address.street_name
|
110
|
+
builder.InstdAmt('%.2f' % transaction.amount, Ccy: transaction.currency)
|
111
|
+
builder.DbtrAgt do
|
112
|
+
builder.FinInstnId do
|
113
|
+
builder.ClrSysMmbId do
|
114
|
+
builder.MmbId(clearing_system_member_id_from_iban(transaction.iban))
|
129
115
|
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
builder.Dbtr do
|
119
|
+
builder.Nm(transaction.name)
|
120
|
+
if transaction.debtor_address
|
121
|
+
builder.PstlAdr do
|
122
|
+
# Only set the fields that are actually provided.
|
123
|
+
# StrtNm, BldgNb, PstCd, TwnNm provide a structured address
|
124
|
+
# separated into its individual fields.
|
125
|
+
# AdrLine provides the address in free format text.
|
126
|
+
# Both are currently allowed and the actual preference depends on the bank.
|
127
|
+
# Also the fields that are required legally may vary depending on the country
|
128
|
+
# or change over time.
|
129
|
+
if transaction.debtor_address.street_name
|
130
|
+
builder.StrtNm transaction.debtor_address.street_name
|
131
|
+
end
|
130
132
|
|
131
|
-
|
132
|
-
|
133
|
-
|
133
|
+
if transaction.debtor_address.post_code
|
134
|
+
builder.PstCd transaction.debtor_address.post_code
|
135
|
+
end
|
134
136
|
|
135
|
-
|
136
|
-
|
137
|
-
|
137
|
+
if transaction.debtor_address.town_name
|
138
|
+
builder.TwnNm transaction.debtor_address.town_name
|
139
|
+
end
|
138
140
|
|
139
|
-
|
140
|
-
|
141
|
-
|
141
|
+
if transaction.debtor_address.country_code
|
142
|
+
builder.Ctry transaction.debtor_address.country_code
|
143
|
+
end
|
142
144
|
|
143
|
-
|
144
|
-
|
145
|
-
|
145
|
+
if transaction.debtor_address.address_line1
|
146
|
+
builder.AdrLine transaction.debtor_address.address_line1
|
147
|
+
end
|
146
148
|
|
147
|
-
|
148
|
-
|
149
|
+
if transaction.debtor_address.address_line2
|
150
|
+
builder.AdrLine transaction.debtor_address.address_line2
|
151
|
+
end
|
149
152
|
end
|
150
153
|
end
|
151
154
|
end
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
end
|
157
|
-
end
|
158
|
-
builder.RmtInf do
|
159
|
-
if transaction.remittance_information
|
160
|
-
builder.Ustrd(transaction.remittance_information)
|
155
|
+
builder.DbtrAcct do
|
156
|
+
builder.Id do
|
157
|
+
builder.IBAN(transaction.iban)
|
158
|
+
end
|
161
159
|
end
|
160
|
+
builder.RmtInf do
|
161
|
+
if transaction.remittance_information
|
162
|
+
builder.Ustrd(transaction.remittance_information)
|
163
|
+
end
|
162
164
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
165
|
+
builder.Strd do
|
166
|
+
builder.CdtrRefInf do
|
167
|
+
builder.Tp do
|
168
|
+
builder.CdOrPrtry do
|
169
|
+
builder.Prtry(transaction.structured_remittance_information.proprietary)
|
170
|
+
end
|
168
171
|
end
|
172
|
+
builder.Ref(transaction.structured_remittance_information.reference)
|
169
173
|
end
|
170
|
-
builder.Ref(transaction.structured_remittance_information.reference)
|
171
174
|
end
|
172
175
|
end
|
173
176
|
end
|
174
177
|
end
|
175
|
-
|
178
|
+
|
176
179
|
end
|
177
180
|
end
|