View | Details | Raw Unified | Return to bug 16330
Collapse All | Expand All

(-)a/Koha/Exceptions/Category.pm (-17 lines)
Lines 1-17 Link Here
1
package Koha::Exceptions::Category;
2
3
use Modern::Perl;
4
5
use Exception::Class (
6
7
    'Koha::Exceptions::Category' => {
8
        description => 'Something went wrong!',
9
    },
10
    'Koha::Exceptions::Category::CategorycodeNotFound' => {
11
        isa => 'Koha::Exceptions::Category',
12
        description => "Category does not exist",
13
        fields => ["categorycode"],
14
    },
15
);
16
17
1;
(-)a/Koha/Exceptions/Library.pm (-17 lines)
Lines 1-17 Link Here
1
package Koha::Exceptions::Library;
2
3
use Modern::Perl;
4
5
use Exception::Class (
6
7
    'Koha::Exceptions::Library' => {
8
        description => 'Something went wrong!',
9
    },
10
    'Koha::Exceptions::Library::BranchcodeNotFound' => {
11
        isa => 'Koha::Exceptions::Library',
12
        description => "Library does not exist",
13
        fields => ["branchcode"],
14
    },
15
);
16
17
1;
(-)a/Koha/Exceptions/Patron.pm (-17 lines)
Lines 1-17 Link Here
1
package Koha::Exceptions::Patron;
2
3
use Modern::Perl;
4
5
use Exception::Class (
6
7
    'Koha::Exceptions::Patron' => {
8
        description => 'Something went wrong!',
9
    },
10
    'Koha::Exceptions::Patron::DuplicateObject' => {
11
        isa => 'Koha::Exceptions::Patron',
12
        description => "Patron cardnumber and userid must be unique",
13
        fields => ["conflict"],
14
    },
15
);
16
17
1;
(-)a/Koha/Patron.pm (-127 / +2 lines)
Lines 2-8 package Koha::Patron; Link Here
2
2
3
# Copyright ByWater Solutions 2014
3
# Copyright ByWater Solutions 2014
4
# Copyright PTFS Europe 2016
4
# Copyright PTFS Europe 2016
5
# Copyright Koha-Suomi Oy 2017
6
#
5
#
7
# This file is part of Koha.
6
# This file is part of Koha.
8
#
7
#
Lines 30-40 use Koha::Database; Link Here
30
use Koha::DateUtils;
29
use Koha::DateUtils;
31
use Koha::Holds;
30
use Koha::Holds;
32
use Koha::Old::Checkouts;
31
use Koha::Old::Checkouts;
33
use Koha::Exceptions;
34
use Koha::Exceptions::Category;
35
use Koha::Exceptions::Library;
36
use Koha::Exceptions::Patron;
37
use Koha::Libraries;
38
use Koha::Patron::Categories;
32
use Koha::Patron::Categories;
39
use Koha::Patron::HouseboundProfile;
33
use Koha::Patron::HouseboundProfile;
40
use Koha::Patron::HouseboundRole;
34
use Koha::Patron::HouseboundRole;
Lines 676-694 sub account_locked { Link Here
676
          and $self->login_attempts >= $FailedLoginAttempts )? 1 : 0;
670
          and $self->login_attempts >= $FailedLoginAttempts )? 1 : 0;
677
}
671
}
678
672
679
=head3 store
673
=head2 Internal methods
680
681
=cut
682
683
sub store {
684
    my ($self) = @_;
685
686
    # $self->_validate();
687
688
    return $self->SUPER::store();
689
}
690
674
691
=head3 type
675
=head3 _type
692
676
693
=cut
677
=cut
694
678
Lines 696-810 sub _type { Link Here
696
    return 'Borrower';
680
    return 'Borrower';
697
}
681
}
698
682
699
=head2 Internal methods
700
701
=head3 _check_branchcode
702
703
Checks the existence of patron's branchcode and throws
704
Koha::Exceptions::Library::BranchcodeNotFound if branchcode is not found.
705
706
=cut
707
708
sub _check_branchcode {
709
    my ($self) = @_;
710
711
    return unless $self->branchcode;
712
    unless (Koha::Libraries->find($self->branchcode)) {
713
        Koha::Exceptions::Library::BranchcodeNotFound->throw(
714
            error => "Library does not exist",
715
            branchcode => $self->branchcode,
716
        );
717
    }
718
    return 1;
719
}
720
721
=head3 _check_categorycode
722
723
Checks the existence of patron's categorycode and throws
724
Koha::Exceptions::Category::CategorycodeNotFound if categorycode is not found.
725
726
=cut
727
728
sub _check_categorycode {
729
    my ($self) = @_;
730
731
    return unless $self->categorycode;
732
    unless (Koha::Patron::Categories->find($self->categorycode)) {
733
        Koha::Exceptions::Category::CategorycodeNotFound->throw(
734
            error => "Patron category does not exist",
735
            categorycode => $self->categorycode,
736
        );
737
    }
738
    return 1;
739
}
740
741
=head3 _check_uniqueness
742
743
Checks patron's cardnumber and userid for uniqueness and throws
744
Koha::Exceptions::Patron::DuplicateObject if conflicting with another patron.
745
746
=cut
747
748
sub _check_uniqueness {
749
    my ($self) = @_;
750
751
    my $select = {};
752
    $select->{cardnumber} = $self->cardnumber if $self->cardnumber;
753
    $select->{userid} = $self->userid if $self->userid;
754
755
    return unless keys %$select;
756
757
    # Find conflicting patrons
758
    my $patrons = Koha::Patrons->search({
759
        '-or' => $select
760
    });
761
762
    if ($patrons->count) {
763
        my $conflict = {};
764
        foreach my $patron ($patrons->as_list) {
765
            # New patron $self: a conflicting patron $patron found.
766
            # Updating patron $self: first make sure conflicting patron $patron is
767
            #                        not this patron $self.
768
            if (!$self->in_storage || $self->in_storage &&
769
            $self->borrowernumber != $patron->borrowernumber) {
770
                # Populate conflict information to exception
771
                if ($patron->cardnumber && $self->cardnumber &&
772
                    $patron->cardnumber eq $self->cardnumber)
773
                {
774
                    $conflict->{cardnumber} = $self->cardnumber;
775
                }
776
                if ($patron->userid && $self->userid &&
777
                    $patron->userid eq $self->userid)
778
                {
779
                    $conflict->{userid} = $self->userid;
780
                }
781
            }
782
        }
783
784
        Koha::Exceptions::Patron::DuplicateObject->throw(
785
            error => "Patron data conflicts with another patron",
786
            conflict => $conflict
787
        ) if keys %$conflict;
788
    }
789
    return 1;
790
}
791
792
=head3 _validate
793
794
Performs a set of validations on this object and throws Koha::Exceptions if errors
795
are found.
796
797
=cut
798
799
sub _validate {
800
    my ($self) = @_;
801
802
    $self->_check_branchcode;
803
    $self->_check_categorycode;
804
    $self->_check_uniqueness;
805
    return $self;
806
}
807
808
=head1 AUTHOR
683
=head1 AUTHOR
809
684
810
Kyle M Hall <kyle@bywatersolutions.com>
685
Kyle M Hall <kyle@bywatersolutions.com>
(-)a/Koha/REST/V1/Patrons.pm (-29 / +24 lines)
Lines 90-98 sub add { Link Here
90
    my $c = shift->openapi->valid_input or return;
90
    my $c = shift->openapi->valid_input or return;
91
91
92
    return try {
92
    return try {
93
        my $body = $c->validation->param('body');
94
93
95
        Koha::Patron->new($body)->_validate;
94
        my $body = _to_model($c->validation->param('body'));
96
95
97
        # TODO: Use AddMember until it has been moved to Koha-namespace
96
        # TODO: Use AddMember until it has been moved to Koha-namespace
98
        my $borrowernumber = AddMember(%$body);
97
        my $borrowernumber = AddMember(%$body);
Lines 109-130 sub add { Link Here
109
                }
108
                }
110
            );
109
            );
111
        }
110
        }
112
        if ( $_->isa('Koha::Exceptions::Patron::DuplicateObject') ) {
111
        if ( $_->isa('Koha::Exceptions::Object::DuplicateID') ) {
113
            return $c->render(
112
            return $c->render(
114
                status  => 409,
113
                status  => 409,
115
                openapi => { error => $_->error, conflict => $_->conflict }
114
                openapi => { error => $_->error, conflict => $_->duplicate_id }
116
            );
115
            );
117
        }
116
        }
118
        elsif ( $_->isa('Koha::Exceptions::Library::BranchcodeNotFound') ) {
117
        elsif ( $_->isa('Koha::Exceptions::Object::FKConstraint') ) {
119
            return $c->render(
118
            return $c->render(
120
                status  => 400,
119
                status  => 400,
121
                openapi => { error => "Given branchcode does not exist" }
120
                openapi => { error => "Given " . $_->broken_fk . " does not exist" }
122
            );
121
            );
123
        }
122
        }
124
        elsif ( $_->isa('Koha::Exceptions::Category::CategorycodeNotFound') ) {
123
        elsif ( $_->isa('Koha::Exceptions::BadParameter') ) {
125
            return $c->render(
124
            return $c->render(
126
                status  => 400,
125
                status  => 400,
127
                openapi => { error => "Given categorycode does not exist" }
126
                openapi => { error => "Given " . $_->parameter . " does not exist" }
128
            );
127
            );
129
        }
128
        }
130
        else {
129
        else {
Lines 147-164 Controller function that handles updating a Koha::Patron object Link Here
147
sub update {
146
sub update {
148
    my $c = shift->openapi->valid_input or return;
147
    my $c = shift->openapi->valid_input or return;
149
148
150
    my $patron = Koha::Patrons->find( $c->validation->param('borrowernumber') );
149
    my $patron_id = $c->validation->param('borrowernumber');
150
    my $patron    = Koha::Patrons->find( $patron_id );
151
151
152
    return try {
152
    unless ($patron) {
153
        my $body = $c->validation->param('body');
153
         return $c->render(
154
             status  => 404,
155
             openapi => { error => "Patron not found" }
156
         );
157
     }
154
158
155
        $patron->set( _to_model($body) )->_validate;
159
    return try {
160
        my $body = _to_model($c->validation->param('body'));
156
161
157
        ## TODO: Use ModMember until it has been moved to Koha-namespace
162
        ## TODO: Use ModMember until it has been moved to Koha-namespace
158
        # Add borrowernumber to $body, as required by ModMember
163
        # Add borrowernumber to $body, as required by ModMember
159
        $body->{borrowernumber} = $patron->borrowernumber;
164
        $body->{borrowernumber} = $patron_id;
160
165
161
        if ( ModMember(%$body) ) {
166
        if ( ModMember(%$body) ) {
167
            # Fetch the updated Koha::Patron object
168
            $patron->discard_changes;
162
            return $c->render( status => 200, openapi => $patron );
169
            return $c->render( status => 200, openapi => $patron );
163
        }
170
        }
164
        else {
171
        else {
Lines 171-182 sub update { Link Here
171
        }
178
        }
172
    }
179
    }
173
    catch {
180
    catch {
174
        unless ($patron) {
175
            return $c->render(
176
                status  => 404,
177
                openapi => { error => "Patron not found" }
178
            );
179
        }
180
        unless ( blessed $_ && $_->can('rethrow') ) {
181
        unless ( blessed $_ && $_->can('rethrow') ) {
181
            return $c->render(
182
            return $c->render(
182
                status  => 500,
183
                status  => 500,
Lines 185-206 sub update { Link Here
185
                }
186
                }
186
            );
187
            );
187
        }
188
        }
188
        if ( $_->isa('Koha::Exceptions::Patron::DuplicateObject') ) {
189
        if ( $_->isa('Koha::Exceptions::Object::DuplicateID') ) {
189
            return $c->render(
190
            return $c->render(
190
                status  => 409,
191
                status  => 409,
191
                openapi => { error => $_->error, conflict => $_->conflict }
192
                openapi => { error => $_->error, conflict => $_->duplicate_id }
192
            );
193
        }
194
        elsif ( $_->isa('Koha::Exceptions::Library::BranchcodeNotFound') ) {
195
            return $c->render(
196
                status  => 400,
197
                openapi => { error => "Given branchcode does not exist" }
198
            );
193
            );
199
        }
194
        }
200
        elsif ( $_->isa('Koha::Exceptions::Category::CategorycodeNotFound') ) {
195
        elsif ( $_->isa('Koha::Exceptions::Object::FKConstraint') ) {
201
            return $c->render(
196
            return $c->render(
202
                status  => 400,
197
                status  => 400,
203
                openapi => { error => "Given categorycode does not exist" }
198
                openapi => { error => "Given " . $_->broken_fk . " does not exist" }
204
            );
199
            );
205
        }
200
        }
206
        elsif ( $_->isa('Koha::Exceptions::MissingParameter') ) {
201
        elsif ( $_->isa('Koha::Exceptions::MissingParameter') ) {
(-)a/t/db_dependent/Koha/Patrons.t (-95 / +1 lines)
Lines 19-25 Link Here
19
19
20
use Modern::Perl;
20
use Modern::Perl;
21
21
22
use Test::More tests => 23;
22
use Test::More tests => 22;
23
use Test::Warn;
23
use Test::Warn;
24
use Time::Fake;
24
use Time::Fake;
25
use DateTime;
25
use DateTime;
Lines 929-1025 $retrieved_patron_1->delete; Link Here
929
is( Koha::Patrons->search->count, $nb_of_patrons + 1, 'Delete should have deleted the patron' );
929
is( Koha::Patrons->search->count, $nb_of_patrons + 1, 'Delete should have deleted the patron' );
930
930
931
$schema->storage->txn_rollback;
931
$schema->storage->txn_rollback;
932
933
subtest '_validate() tests' => sub {
934
    plan tests => 4;
935
936
    $schema->storage->txn_begin;
937
938
    Koha::Patrons->delete;
939
940
    my $categorycode = $builder->build({ source => 'Category' })->{categorycode};
941
    my $branchcode = $builder->build({ source => 'Branch' })->{branchcode};
942
    my $patron = $builder->build({
943
        source => 'Borrower',
944
        value => {
945
            branchcode   => $branchcode,
946
            cardnumber   => 'conflict',
947
            categorycode => $categorycode,
948
        }
949
    });
950
951
    ok(Koha::Patron->new({
952
        surname      => 'Store test',
953
        branchcode   => $branchcode,
954
        categorycode => $categorycode
955
    })->_validate->store, 'Stored a patron');
956
957
    subtest '_check_categorycode' => sub {
958
        plan tests => 2;
959
960
        my $conflicting = $builder->build({
961
            source => 'Borrower',
962
            value => {
963
                branchcode   => $branchcode,
964
                categorycode => 'nonexistent',
965
            }
966
        });
967
        delete $conflicting->{borrowernumber};
968
969
        eval { Koha::Patron->new($conflicting)->_validate };
970
971
        isa_ok($@, "Koha::Exceptions::Category::CategorycodeNotFound");
972
        is($@->{categorycode}, $conflicting->{categorycode},
973
           'Exception describes non-existent categorycode');
974
    };
975
976
    subtest '_check_categorycode' => sub {
977
        plan tests => 2;
978
979
        my $conflicting = $builder->build({
980
            source => 'Borrower',
981
            value => {
982
                branchcode   => 'nonexistent',
983
                categorycode => $categorycode,
984
            }
985
        });
986
        delete $conflicting->{borrowernumber};
987
988
        eval { Koha::Patron->new($conflicting)->_validate };
989
990
        isa_ok($@, "Koha::Exceptions::Library::BranchcodeNotFound");
991
        is($@->{branchcode}, $conflicting->{branchcode},
992
           'Exception describes non-existent branchcode');
993
    };
994
995
    subtest '_check_uniqueness() tests' => sub {
996
        plan tests => 4;
997
998
        my $conflicting = $builder->build({
999
            source => 'Borrower',
1000
            value => {
1001
                branchcode   => $branchcode,
1002
                categorycode => $categorycode,
1003
            }
1004
        });
1005
        delete $conflicting->{borrowernumber};
1006
        $conflicting->{cardnumber} = 'conflict';
1007
        $conflicting->{userid} = $patron->{userid};
1008
1009
        eval { Koha::Patron->new($conflicting)->_validate };
1010
1011
        isa_ok($@, "Koha::Exceptions::Patron::DuplicateObject");
1012
        is($@->{conflict}->{cardnumber}, $conflicting->{cardnumber},
1013
           'Exception describes conflicting cardnumber');
1014
        is($@->{conflict}->{userid}, $conflicting->{userid},
1015
           'Exception describes conflicting userid');
1016
1017
        $conflicting->{cardnumber} = 'notconflicting';
1018
        $conflicting->{userid}     = 'notconflicting';
1019
1020
        ok(Koha::Patron->new($conflicting)->_validate->store, 'After modifying'
1021
           .' cardnumber and userid to not conflict with others, no exception.');
1022
    };
1023
1024
    $schema->storage->txn_rollback;
1025
};
(-)a/t/db_dependent/api/v1/patrons.t (-29 / +31 lines)
Lines 19-24 use Modern::Perl; Link Here
19
19
20
use Test::More tests => 5;
20
use Test::More tests => 5;
21
use Test::Mojo;
21
use Test::Mojo;
22
use Test::Warn;
22
23
23
use t::lib::TestBuilder;
24
use t::lib::TestBuilder;
24
use t::lib::Mocks;
25
use t::lib::Mocks;
Lines 160-166 subtest 'add() tests' => sub { Link Here
160
    unauthorized_access_tests('POST', undef, $newpatron);
161
    unauthorized_access_tests('POST', undef, $newpatron);
161
162
162
    subtest 'librarian access tests' => sub {
163
    subtest 'librarian access tests' => sub {
163
        plan tests => 18;
164
        plan tests => 20;
164
165
165
        my ($borrowernumber, $sessionid) = create_user_and_session({
166
        my ($borrowernumber, $sessionid) = create_user_and_session({
166
            authorized => 1 });
167
            authorized => 1 });
Lines 168-176 subtest 'add() tests' => sub { Link Here
168
        $newpatron->{branchcode} = "nonexistent"; # Test invalid branchcode
169
        $newpatron->{branchcode} = "nonexistent"; # Test invalid branchcode
169
        my $tx = $t->ua->build_tx(POST => "/api/v1/patrons" => json => $newpatron );
170
        my $tx = $t->ua->build_tx(POST => "/api/v1/patrons" => json => $newpatron );
170
        $tx->req->cookies({name => 'CGISESSID', value => $sessionid});
171
        $tx->req->cookies({name => 'CGISESSID', value => $sessionid});
171
        $t->request_ok($tx)
172
        warning_like {
172
          ->status_is(400)
173
            $t->request_ok($tx)
173
          ->json_is('/error' => "Given branchcode does not exist");
174
              ->status_is(400)
175
              ->json_is('/error' => "Given branchcode does not exist"); }
176
            qr/^DBD::mysql::st execute failed: Cannot add or update a child row: a foreign key constraint fails/;
177
174
        $newpatron->{branchcode} = $branchcode;
178
        $newpatron->{branchcode} = $branchcode;
175
179
176
        $newpatron->{categorycode} = "nonexistent"; # Test invalid patron category
180
        $newpatron->{categorycode} = "nonexistent"; # Test invalid patron category
Lines 200-214 subtest 'add() tests' => sub { Link Here
200
204
201
        $tx = $t->ua->build_tx(POST => "/api/v1/patrons" => json => $newpatron);
205
        $tx = $t->ua->build_tx(POST => "/api/v1/patrons" => json => $newpatron);
202
        $tx->req->cookies({name => 'CGISESSID', value => $sessionid});
206
        $tx->req->cookies({name => 'CGISESSID', value => $sessionid});
203
        $t->request_ok($tx)
207
        warning_like {
204
          ->status_is(409)
208
            $t->request_ok($tx)
205
          ->json_has('/error', 'Fails when trying to POST duplicate'.
209
              ->status_is(409)
206
                     ' cardnumber or userid')
210
              ->json_has( '/error', 'Fails when trying to POST duplicate cardnumber' )
207
          ->json_has('/conflict', {
211
              ->json_has( '/conflict', 'cardnumber' ); }
208
                        userid => $newpatron->{ userid },
212
            qr/^DBD::mysql::st execute failed: Duplicate entry '(.*?)' for key 'cardnumber'/;
209
                        cardnumber => $newpatron->{ cardnumber }
210
                    }
211
            );
212
    };
213
    };
213
214
214
    $schema->storage->txn_rollback;
215
    $schema->storage->txn_rollback;
Lines 222-228 subtest 'update() tests' => sub { Link Here
222
    unauthorized_access_tests('PUT', 123, {email => 'nobody@example.com'});
223
    unauthorized_access_tests('PUT', 123, {email => 'nobody@example.com'});
223
224
224
    subtest 'librarian access tests' => sub {
225
    subtest 'librarian access tests' => sub {
225
        plan tests => 20;
226
        plan tests => 23;
226
227
227
        t::lib::Mocks::mock_preference('minPasswordLength', 1);
228
        t::lib::Mocks::mock_preference('minPasswordLength', 1);
228
        my ($borrowernumber, $sessionid) = create_user_and_session({ authorized => 1 });
229
        my ($borrowernumber, $sessionid) = create_user_and_session({ authorized => 1 });
Lines 243-259 subtest 'update() tests' => sub { Link Here
243
        $newpatron->{categorycode} = 'nonexistent';
244
        $newpatron->{categorycode} = 'nonexistent';
244
        $tx = $t->ua->build_tx(PUT => "/api/v1/patrons/$borrowernumber2" => json => $newpatron );
245
        $tx = $t->ua->build_tx(PUT => "/api/v1/patrons/$borrowernumber2" => json => $newpatron );
245
        $tx->req->cookies({name => 'CGISESSID', value => $sessionid});
246
        $tx->req->cookies({name => 'CGISESSID', value => $sessionid});
246
        $t->request_ok($tx)
247
        warning_like {
247
          ->status_is(400)
248
            $t->request_ok($tx)
248
          ->json_is('/error' => "Given categorycode does not exist");
249
              ->status_is(400)
250
              ->json_is('/error' => "Given categorycode does not exist"); }
251
            qr/^DBD::mysql::st execute failed: Cannot add or update a child row: a foreign key constraint fails/;
249
        $newpatron->{categorycode} = $patron_2->categorycode;
252
        $newpatron->{categorycode} = $patron_2->categorycode;
250
253
251
        $newpatron->{branchcode} = 'nonexistent';
254
        $newpatron->{branchcode} = 'nonexistent';
252
        $tx = $t->ua->build_tx(PUT => "/api/v1/patrons/$borrowernumber2" => json => $newpatron );
255
        $tx = $t->ua->build_tx(PUT => "/api/v1/patrons/$borrowernumber2" => json => $newpatron );
253
        $tx->req->cookies({name => 'CGISESSID', value => $sessionid});
256
        $tx->req->cookies({name => 'CGISESSID', value => $sessionid});
254
        $t->request_ok($tx)
257
        warning_like {
255
          ->status_is(400)
258
            $t->request_ok($tx)
256
          ->json_is('/error' => "Given branchcode does not exist");
259
              ->status_is(400)
260
              ->json_is('/error' => "Given branchcode does not exist"); }
261
            qr/^DBD::mysql::st execute failed: Cannot add or update a child row: a foreign key constraint fails/;
257
        $newpatron->{branchcode} = $patron_2->branchcode;
262
        $newpatron->{branchcode} = $patron_2->branchcode;
258
263
259
        $newpatron->{falseproperty} = "Non existent property";
264
        $newpatron->{falseproperty} = "Non existent property";
Lines 271-284 subtest 'update() tests' => sub { Link Here
271
276
272
        $tx = $t->ua->build_tx( PUT => "/api/v1/patrons/$borrowernumber2" => json => $newpatron );
277
        $tx = $t->ua->build_tx( PUT => "/api/v1/patrons/$borrowernumber2" => json => $newpatron );
273
        $tx->req->cookies({ name => 'CGISESSID', value => $sessionid });
278
        $tx->req->cookies({ name => 'CGISESSID', value => $sessionid });
274
        $t->request_ok($tx)->status_is(409)
279
        warning_like {
275
          ->json_has( '/error' => "Fails when trying to update to an existing cardnumber or userid")
280
            $t->request_ok($tx)
276
          ->json_is(  '/conflict',
281
              ->status_is(409)
277
                        {
282
              ->json_has( '/error' => "Fails when trying to update to an existing cardnumber or userid")
278
                            cardnumber => $newpatron->{cardnumber},
283
              ->json_is(  '/conflict', 'cardnumber' ); }
279
                            userid     => $newpatron->{userid}
284
            qr/^DBD::mysql::st execute failed: Duplicate entry '(.*?)' for key 'cardnumber'/;
280
                        }
281
          );
282
285
283
        $newpatron->{ cardnumber } = $borrowernumber.$borrowernumber2;
286
        $newpatron->{ cardnumber } = $borrowernumber.$borrowernumber2;
284
        $newpatron->{ userid } = "user".$borrowernumber.$borrowernumber2;
287
        $newpatron->{ userid } = "user".$borrowernumber.$borrowernumber2;
285
- 

Return to bug 16330