Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for BBAN credit transfers #112

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/sepa_king/account.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class Account
convert :name, to: :text

validates_length_of :name, within: 1..70
validates_presence_of :iban
validates_with BICValidator, IBANValidator, message: "%{value} is invalid"

def initialize(attributes = {})
Expand Down
33 changes: 32 additions & 1 deletion lib/sepa_king/message/credit_transfer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,20 @@ def build_transaction(builder, transaction)
end
end
end

if transaction.clearing_bank_identifier
builder.CdtrAgt do
builder.FinInstnId do
builder.ClrSysMmbId do
builder.ClrSysId do
builder.Cd(transaction.clearing_code)
end
builder.MmbId(transaction.clearing_bank_identifier)
end
end
end
end

builder.Cdtr do
builder.Nm(transaction.name)
if transaction.creditor_address
Expand Down Expand Up @@ -129,9 +143,26 @@ def build_transaction(builder, transaction)
end
end
end

builder.CdtrAcct do
builder.Id do
builder.IBAN(transaction.iban)
if transaction.iban
builder.IBAN(transaction.iban)
end

if transaction.bban
builder.Othr do
builder.Id(transaction.bban)
builder.SchmeNm do
if transaction.bban_proprietary
builder.Prtry(transaction.bban_proprietary)
else
builder.Cd("BBAN")
end
end
end
end

end
end
if transaction.remittance_information
Expand Down
6 changes: 6 additions & 0 deletions lib/sepa_king/transaction.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ class Transaction
attr_accessor :name,
:iban,
:bic,
:bban, # bankgiro 5748964
:bban_proprietary, # BGNR
:clearing_code, # SESBA
:clearing_bank_identifier, # 9960
:amount,
:instruction,
:reference,
Expand All @@ -31,6 +35,8 @@ class Transaction
validates_presence_of :requested_date
validates_inclusion_of :batch_booking, :in => [true, false]
validates_with BICValidator, IBANValidator, message: "%{value} is invalid"
validates :iban, presence: true, unless: :bban
validates :bban, presence: true, unless: :iban

def initialize(attributes = {})
attributes.each do |name, value|
Expand Down
6 changes: 4 additions & 2 deletions lib/sepa_king/validator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ def validate(record)
field_name = options[:field_name] || :iban
value = record.send(field_name).to_s

unless IBANTools::IBAN.valid?(value) && value.match?(REGEX)
record.errors.add(field_name, :invalid, message: options[:message])
if value.present?
unless IBANTools::IBAN.valid?(value) && value.match?(REGEX)
record.errors.add(field_name, :invalid, message: options[:message])
end
end
end
end
Expand Down
84 changes: 84 additions & 0 deletions spec/credit_transfer_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,90 @@
end
end

context 'with a bankgiro creditor account' do
subject do
sct = credit_transfer

sct.add_transaction name: 'Telekomiker AG',
bban: '123456',
clearing_bank_identifier: '9900', # bankgiro
clearing_code: 'SESBA',
bban_proprietary: 'BGNR',
amount: 102.50,
reference: 'XYZ-1234/123',
remittance_information: 'Rechnung vom 22.08.2013'

sct.to_xml(SEPA::PAIN_001_001_03)
end

it 'should validate against pain.001.001.03' do
expect(subject).to validate_against('pain.001.001.03.xsd')
end

it 'should contain <CdtrAcct/Id/Othr> with expected <Id> and <SchmeNm/Prtry>' do
expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf/CdtTrfTxInf[1]/CdtrAcct/Id/Othr/Id', '123456')
expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf/CdtTrfTxInf[1]/CdtrAcct/Id/Othr/SchmeNm/Prtry', 'BGNR')
end

it 'should contain <ClrSysMmbId> with expected <MmbId> and <ClrSysId/Cd>' do
expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf/CdtTrfTxInf[1]/CdtrAgt/FinInstnId/ClrSysMmbId/MmbId', '9900')
expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf/CdtTrfTxInf[1]/CdtrAgt/FinInstnId/ClrSysMmbId/ClrSysId/Cd', 'SESBA')
end
end

context 'with a plusgiro creditor account' do
subject do
sct = credit_transfer

sct.add_transaction name: 'Telekomiker AG',
bban: '123456',
clearing_bank_identifier: '9960', # plusgiro
clearing_code: 'SESBA',
amount: 102.50,
reference: 'XYZ-1234/123',
remittance_information: 'Rechnung vom 22.08.2013'

sct.to_xml(SEPA::PAIN_001_001_03)
end

it 'should validate against pain.001.001.03' do
expect(subject).to validate_against('pain.001.001.03.xsd')
end

it 'should contain <CdtrAcct/Id/Othr> with expected <Id> and <SchmeNm/Cd>' do
expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf/CdtTrfTxInf[1]/CdtrAcct/Id/Othr/Id', '123456')
expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf/CdtTrfTxInf[1]/CdtrAcct/Id/Othr/SchmeNm/Cd', 'BBAN')
end

it 'should contain <ClrSysMmbId> with expected <MmbId> and <ClrSysId/Cd>' do
expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf/CdtTrfTxInf[1]/CdtrAgt/FinInstnId/ClrSysMmbId/MmbId', '9960')
expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf/CdtTrfTxInf[1]/CdtrAgt/FinInstnId/ClrSysMmbId/ClrSysId/Cd', 'SESBA')
end
end

context 'with a bban creditor account' do
subject do
sct = credit_transfer

sct.add_transaction name: 'Telekomiker AG',
bban: '123456',
amount: 102.50,
reference: 'XYZ-1234/123',
remittance_information: 'Rechnung vom 22.08.2013'

sct.to_xml(SEPA::PAIN_001_001_03)
end

it 'should validate against pain.001.001.03' do
expect(subject).to validate_against('pain.001.001.03.xsd')
end

it 'should contain <CdtrAcct/Id/Othr> with expected <Id> and <SchmeNm/Cd>' do
expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf/CdtTrfTxInf[1]/CdtrAcct/Id/Othr/Id', '123456')
expect(subject).to have_xml('//Document/CstmrCdtTrfInitn/PmtInf/CdtTrfTxInf[1]/CdtrAcct/Id/Othr/SchmeNm/Cd', 'BBAN')
end
end

context 'for valid debtor' do
context 'without BIC (IBAN-only)' do
subject do
Expand Down
2 changes: 1 addition & 1 deletion spec/validator_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class Validatable
end

it 'should not accept an invalid IBAN' do
expect(Validatable).not_to accept('', 'xxx', # Oviously no IBAN
expect(Validatable).not_to accept('xxx', # Oviously no IBAN
'DE22500500009876543210', # wrong checksum
'DE2150050000987654321', # too short
'de87200500001234567890', # downcase characters
Expand Down