Commit bd4fc172 authored by Alasdair Keyes's avatar Alasdair Keyes

Add in IBAN Value Object and tests

parent 266382ee
......@@ -39,6 +39,7 @@ my $invalid_post_code = eval{
* ValueObject::Finance::BIC::Passive
* ValueObject::Finance::BIC::Reverse
* ValueObject::Finance::BIC::Test
* ValueObject::Finance::IBAN
* ValueObject::Gender
* ValueObject::Geography::Country::Codes::2Character
* ValueObject::Geography::Country::Codes::3Character
......
package ValueObject::Finance::IBAN;
use strict;
use warnings;
use Carp;
use ValueObject::Finance::IBAN::Exception::Invalid;
use ValueObject::Geography::Country::Codes::2Character;
use Math::BigInt;
use base qw/ ValueObject::Base /;
my $iban_regex = qr/^([A-Z]{2})(\d{2})([A-Z0-9]{10,30})$/i;
my $iban_character_offset = 55;
sub validate {
my $self = shift;
my $value = shift || '';
if ($value =~ /$iban_regex/) {
my ($country, $checksum, $account_number) = ($1, $2, $3);
## Check country
eval{
ValueObject::Geography::Country::Codes::2Character->new($country);
} || croak (ValueObject::Finance::IBAN::Exception::Invalid->new($value));
## Rearrange into order for checksum
my $number_to_validate = join('',
map { $self->convert_character_to_integer($_) } (
split(//, $account_number),
split(//, $country)
)
) . $checksum;
## Convert to BigInt object
$number_to_validate = Math::BigInt->new($number_to_validate);
my $modulus = $number_to_validate % 97;
return 1
if ($modulus == 1);
}
## Else fail
croak (ValueObject::Finance::IBAN::Exception::Invalid->new($value));
}
sub convert_character_to_integer {
my $self = shift;
my $character = shift;
## return character as is, if not alphabetic
return $character
unless ($character =~ /^[A-Z]$/);
## Expand string to number
return ord($character) - $iban_character_offset;
}
1;
=head1 NAME
ValueObject::Finance::IBAN - Value object to represent IBAN numbers
=head1 SYNOPSIS
use ValueObject::Finance::IBAN;
my $vo = ValueObject::Finance::IBAN->new('GB15MIDL40051512345678');
=head1 DESCRIPTION
ValueObject::Finance::IBAN provides Value Object for validation of IBAN
numbers (Case insensitive)
=head1 METHODS
=over 4
=item new($value = '')
Creates a new object
Throws ValueObject::Finance::IBAN::Exception::Invalid if $value is invalid
=item validate()
Called during instantiation to validate $value
=item value()
Returns the raw value as passed in at instantiation
=back
=head1 SEE ALSO
L<ValueObject::Base>
=cut
package ValueObject::Finance::IBAN::Exception::Invalid;
use strict;
use warnings;
use base qw/ ValueObject::Exception::Base /;
sub new {
my $class = shift;
my $message = 'Invalid IBAN';
return $class->SUPER::new($message, @_);
}
1;
#!/usr/bin/env perl
use strict;
use warnings;
use Test::More;
use Test::Exception;
use FindBin qw($Bin);
use lib "$Bin/../lib";
# Valid IBANs taken from http://www.rbs.co.uk/corporate/international/g0/guide-to-international-business/regulatory-information/iban/iban-example.ashx
# Setup
require_ok('ValueObject::Finance::IBAN');
my @invalid_ibans = qw/
AL47212110090000000235698742
al47212110090000000235698741
/;
my @valid_ibans = qw/
AL47212110090000000235698741
AD1200012030200359100100
AT611904300234573201
AZ21NABZ00000000137010001944
BH67BMAG00001299123456
BE62510007547061
BA391290079401028494
BG80BNBG96611020345678
HR1210010051863000160
CY17002001280000001200527600
CZ6508000000192000145399
DK5000400440116243
EE382200221020145685
FO9754320388899944
FI2112345600000785
FR1420041010050500013M02606
GE29NB0000000101904917
DE89370400440532013000
GI75NWBK000000007099453
GR1601101250000000012300695
GL5604449876543210
HU42117730161111101800000000
IS140159260076545510730339
IE29AIBK93115212345678
IL620108000000099999999
IT40S0542811101000000123456
JO94CBJO0010000000000131000302
KW81CBKU0000000000001234560101
LV80BANK0000435195001
LB62099900000001001901229114
LI21088100002324013AA
LT121000011101001000
LU280019400644750000
MK07250120000058984
MT84MALT011000012345MTLCAST001S
MU17BOMM0101101030300200000MUR
MD24AG000225100013104168
MC9320052222100112233M44555
ME25505000012345678951
NL39RABO0300065264
NO9386011117947
PK36SCBL0000001123456702
PL60102010260000042270201111
PT50000201231234567890154
QA58DOHB00001234567890ABCDEFG
RO49AAAA1B31007593840000
SM86U0322509800000000270100
SA0380000000608010167519
RS35260005601001611379
SK3112000000198742637541
SI56191000000123438
ES8023100001180000012345
SE3550000000054910000003
CH9300762011623852957
TN5910006035183598478831
TR330006100519786457841326
AE070331234567890123456
/;
my $valid_iban = $valid_ibans[0];
my $valid_iban_vo = ValueObject::Finance::IBAN->new($valid_iban);
# Instantiation Test
isa_ok($valid_iban_vo, 'ValueObject::Finance::IBAN');
## Test return values
ok($valid_iban_vo->value() eq $valid_iban, "value function for object '$valid_iban'");
ok($valid_iban_vo eq $valid_iban, "stringify for object '$valid_iban'");
my $test_count = 4;
# Test valid/invalid examples
foreach my $test_valid_iban (@valid_ibans) {
$test_count++;
isa_ok(ValueObject::Finance::IBAN->new($test_valid_iban), 'ValueObject::Finance::IBAN');
}
foreach my $test_invalid_iban (@invalid_ibans) {
$test_count++;
throws_ok { ValueObject::Finance::IBAN->new($test_invalid_iban) }
'ValueObject::Finance::IBAN::Exception::Invalid',
'Invalid exception for ValueObject::Finance::IBAN';
}
done_testing($test_count);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment