From 8520049059c175e48315030a1575dc2478bef80d Mon Sep 17 00:00:00 2001 From: Peter Wall Date: Tue, 7 Sep 2021 15:25:38 +0200 Subject: [PATCH] Add support for BBAN credit transfers --- lib/sepa_king/account.rb | 1 + lib/sepa_king/message/credit_transfer.rb | 33 +++++++++- lib/sepa_king/transaction.rb | 6 ++ lib/sepa_king/validator.rb | 6 +- spec/credit_transfer_spec.rb | 84 ++++++++++++++++++++++++ spec/validator_spec.rb | 2 +- 6 files changed, 128 insertions(+), 4 deletions(-) diff --git a/lib/sepa_king/account.rb b/lib/sepa_king/account.rb index 8a2b5d5..5d1cba5 100644 --- a/lib/sepa_king/account.rb +++ b/lib/sepa_king/account.rb @@ -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 = {}) diff --git a/lib/sepa_king/message/credit_transfer.rb b/lib/sepa_king/message/credit_transfer.rb index bddd701..be51fbb 100644 --- a/lib/sepa_king/message/credit_transfer.rb +++ b/lib/sepa_king/message/credit_transfer.rb @@ -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 @@ -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 diff --git a/lib/sepa_king/transaction.rb b/lib/sepa_king/transaction.rb index d6707e4..462d1c2 100644 --- a/lib/sepa_king/transaction.rb +++ b/lib/sepa_king/transaction.rb @@ -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, @@ -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| diff --git a/lib/sepa_king/validator.rb b/lib/sepa_king/validator.rb index 1ab0a6f..8afb146 100644 --- a/lib/sepa_king/validator.rb +++ b/lib/sepa_king/validator.rb @@ -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 diff --git a/spec/credit_transfer_spec.rb b/spec/credit_transfer_spec.rb index 3518279..b54e06e 100644 --- a/spec/credit_transfer_spec.rb +++ b/spec/credit_transfer_spec.rb @@ -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 with expected and ' 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 with expected and ' 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 with expected and ' 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 with expected and ' 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 with expected and ' 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 diff --git a/spec/validator_spec.rb b/spec/validator_spec.rb index 66e3213..6752562 100644 --- a/spec/validator_spec.rb +++ b/spec/validator_spec.rb @@ -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