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

(-)a/Koha/Exceptions/Patron/Relationship.pm (-13 / +10 lines)
Lines 32-38 use Exception::Class ( Link Here
32
    'Koha::Exceptions::Patron::Relationship::InvalidRelationship' => {
32
    'Koha::Exceptions::Patron::Relationship::InvalidRelationship' => {
33
        isa         => 'Koha::Exceptions::Patron::Relationship',
33
        isa         => 'Koha::Exceptions::Patron::Relationship',
34
        description => 'The specified relationship is invalid',
34
        description => 'The specified relationship is invalid',
35
        fields => [ 'relationship', 'no_relationship', 'invalid_guarantor' ]
35
        fields      => [ 'relationship', 'no_relationship', 'invalid_guarantor' ]
36
    },
36
    },
37
    'Koha::Exceptions::Patron::Relationship::NoGuarantor' => {
37
    'Koha::Exceptions::Patron::Relationship::NoGuarantor' => {
38
        isa         => 'Koha::Exceptions::Patron::Relationship',
38
        isa         => 'Koha::Exceptions::Patron::Relationship',
Lines 45-67 sub full_message { Link Here
45
45
46
    my $msg = $self->message;
46
    my $msg = $self->message;
47
47
48
    unless ( $msg) {
48
    unless ($msg) {
49
        if ( $self->isa('Koha::Exceptions::Patron::Relationship::InvalidRelationship') ) {
49
        if ( $self->isa('Koha::Exceptions::Patron::Relationship::InvalidRelationship') ) {
50
            if ( $self->no_relationship ) {
50
            if ( $self->no_relationship ) {
51
                $msg = sprintf( "No relationship passed." );
51
                $msg = sprintf("No relationship passed.");
52
            }
52
            } elsif ( $self->invalid_guarantor ) {
53
            elsif ( $self->invalid_guarantor ) {
54
                $msg = sprintf("Child patron cannot be a guarantor.");
53
                $msg = sprintf("Child patron cannot be a guarantor.");
54
            } else {
55
                $msg = sprintf( "Invalid relationship passed, '%s' is not defined.", $self->relationship );
55
            }
56
            }
56
            else {
57
        } elsif ( $self->isa('Koha::Exceptions::Patron::Relationship::DuplicateRelationship') ) {
57
                $msg = sprintf("Invalid relationship passed, '%s' is not defined.", $self->relationship );
58
            $msg = sprintf(
58
            }
59
        }
60
        elsif ( $self->isa('Koha::Exceptions::Patron::Relationship::DuplicateRelationship') ) {
61
            $msg
62
                = sprintf(
63
                "There already exists a relationship for the same guarantor (%s) and guarantee (%s) combination",
59
                "There already exists a relationship for the same guarantor (%s) and guarantee (%s) combination",
64
                $self->guarantor_id, $self->guarantee_id );
60
                $self->guarantor_id, $self->guarantee_id
61
            );
65
        }
62
        }
66
    }
63
    }
67
64
(-)a/Koha/Patron.pm (-54 / +49 lines)
Lines 191-197 to db Link Here
191
=cut
191
=cut
192
192
193
sub store {
193
sub store {
194
    my $self = shift;
194
    my $self   = shift;
195
    my $params = @_ ? shift : {};
195
    my $params = @_ ? shift : {};
196
196
197
    my $guarantors = $params->{guarantors} // [];
197
    my $guarantors = $params->{guarantors} // [];
Lines 202-208 sub store { Link Here
202
                C4::Context->preference("autoMemberNum")
202
                C4::Context->preference("autoMemberNum")
203
                and ( not defined $self->cardnumber
203
                and ( not defined $self->cardnumber
204
                    or $self->cardnumber eq '' )
204
                    or $self->cardnumber eq '' )
205
              )
205
                )
206
            {
206
            {
207
                # Warning: The caller is responsible for locking the members table in write
207
                # Warning: The caller is responsible for locking the members table in write
208
                # mode, to avoid database corruption.
208
                # mode, to avoid database corruption.
Lines 210-216 sub store { Link Here
210
                $self->fixup_cardnumber;
210
                $self->fixup_cardnumber;
211
            }
211
            }
212
212
213
            unless( $self->category->in_storage ) {
213
            unless ( $self->category->in_storage ) {
214
                Koha::Exceptions::Object::FKConstraint->throw(
214
                Koha::Exceptions::Object::FKConstraint->throw(
215
                    broken_fk => 'categorycode',
215
                    broken_fk => 'categorycode',
216
                    value     => $self->categorycode,
216
                    value     => $self->categorycode,
Lines 221-235 sub store { Link Here
221
221
222
            my $new_cardnumber = $self->cardnumber;
222
            my $new_cardnumber = $self->cardnumber;
223
            Koha::Plugins->call( 'patron_barcode_transform', \$new_cardnumber );
223
            Koha::Plugins->call( 'patron_barcode_transform', \$new_cardnumber );
224
            $self->cardnumber( $new_cardnumber );
224
            $self->cardnumber($new_cardnumber);
225
225
226
            # Set surname to uppercase if uppercasesurname is true
226
            # Set surname to uppercase if uppercasesurname is true
227
            $self->surname( uc($self->surname) )
227
            $self->surname( uc( $self->surname ) )
228
                if C4::Context->preference("uppercasesurnames");
228
                if C4::Context->preference("uppercasesurnames");
229
229
230
            $self->relationship(undef) # We do not want to store an empty string in this field
230
            $self->relationship(undef)    # We do not want to store an empty string in this field
231
              if defined $self->relationship
231
                if defined $self->relationship
232
                     and $self->relationship eq "";
232
                and $self->relationship eq "";
233
233
234
            unless ( $self->in_storage ) {    #AddMember
234
            unless ( $self->in_storage ) {    #AddMember
235
235
Lines 251-268 sub store { Link Here
251
                # Set the privacy depending on the patron's category
251
                # Set the privacy depending on the patron's category
252
                my $default_privacy = $self->category->default_privacy || q{};
252
                my $default_privacy = $self->category->default_privacy || q{};
253
                $default_privacy =
253
                $default_privacy =
254
                    $default_privacy eq 'default' ? 1
254
                      $default_privacy eq 'default' ? 1
255
                  : $default_privacy eq 'never'   ? 2
255
                    : $default_privacy eq 'never'   ? 2
256
                  : $default_privacy eq 'forever' ? 0
256
                    : $default_privacy eq 'forever' ? 0
257
                  :                                                   undef;
257
                    :                                 undef;
258
                $self->privacy($default_privacy);
258
                $self->privacy($default_privacy);
259
259
260
                # Call any check_password plugins if password is passed
260
                # Call any check_password plugins if password is passed
261
                if ( C4::Context->config("enable_plugins") && $self->password ) {
261
                if ( C4::Context->config("enable_plugins") && $self->password ) {
262
                    my @plugins = Koha::Plugins->new()->GetPlugins({
262
                    my @plugins = Koha::Plugins->new()->GetPlugins(
263
                        method => 'check_password',
263
                        {
264
                    });
264
                            method => 'check_password',
265
                    foreach my $plugin ( @plugins ) {
265
                        }
266
                    );
267
                    foreach my $plugin (@plugins) {
268
266
                        # This plugin hook will also be used by a plugin for the Norwegian national
269
                        # This plugin hook will also be used by a plugin for the Norwegian national
267
                        # patron database. This is why we need to pass both the password and the
270
                        # patron database. This is why we need to pass both the password and the
268
                        # borrowernumber to the plugin.
271
                        # borrowernumber to the plugin.
Lines 281-297 sub store { Link Here
281
                # Make a copy of the plain text password for later use
284
                # Make a copy of the plain text password for later use
282
                $self->plain_text_password( $self->password );
285
                $self->plain_text_password( $self->password );
283
286
284
                $self->password_expiration_date( $self->password
287
                $self->password_expiration_date(
288
                      $self->password
285
                    ? $self->category->get_password_expiry_date || undef
289
                    ? $self->category->get_password_expiry_date || undef
286
                    : undef );
290
                    : undef
291
                );
292
287
                # Create a disabled account if no password provided
293
                # Create a disabled account if no password provided
288
                $self->password( $self->password
294
                $self->password(
295
                    $self->password
289
                    ? Koha::AuthUtils::hash_password( $self->password )
296
                    ? Koha::AuthUtils::hash_password( $self->password )
290
                    : '!' );
297
                    : '!'
298
                );
291
299
292
                $self->borrowernumber(undef);
300
                $self->borrowernumber(undef);
293
301
294
                if ( C4::Context->preference('ChildNeedsGuarantor')
302
                if (    C4::Context->preference('ChildNeedsGuarantor')
295
                    and ( $self->is_child or $self->category->can_be_guarantee )
303
                    and ( $self->is_child or $self->category->can_be_guarantee )
296
                    and $self->contactname eq ""
304
                    and $self->contactname eq ""
297
                    and !@$guarantors )
305
                    and !@$guarantors )
Lines 300-308 sub store { Link Here
300
                }
308
                }
301
309
302
                foreach my $guarantor (@$guarantors) {
310
                foreach my $guarantor (@$guarantors) {
303
                    if ( $guarantor->is_child or $guarantor->category->can_be_guarantee) {
311
                    if ( $guarantor->is_child or $guarantor->category->can_be_guarantee ) {
304
                        Koha::Exceptions::Patron::Relationship::InvalidRelationship
312
                        Koha::Exceptions::Patron::Relationship::InvalidRelationship->throw( invalid_guarantor => 1 );
305
                        ->throw( invalid_guarantor => 1 );
306
                    }
313
                    }
307
                }
314
                }
308
315
Lines 311-360 sub store { Link Here
311
                $self->add_enrolment_fee_if_needed(0);
318
                $self->add_enrolment_fee_if_needed(0);
312
319
313
                logaction( "MEMBERS", "CREATE", $self->borrowernumber, "" )
320
                logaction( "MEMBERS", "CREATE", $self->borrowernumber, "" )
314
                  if C4::Context->preference("BorrowersLog");
321
                    if C4::Context->preference("BorrowersLog");
315
            }
322
            } else {    #ModMember
316
            else {    #ModMember
317
323
318
                my $self_from_storage = $self->get_from_storage;
324
                my $self_from_storage = $self->get_from_storage;
319
325
320
                # Do not accept invalid userid here
326
                # Do not accept invalid userid here
321
                $self->generate_userid unless $self->userid;
327
                $self->generate_userid unless $self->userid;
322
                Koha::Exceptions::Patron::InvalidUserid->throw( userid => $self->userid )
328
                Koha::Exceptions::Patron::InvalidUserid->throw( userid => $self->userid )
323
                      unless $self->has_valid_userid;
329
                    unless $self->has_valid_userid;
324
330
325
                # If a borrower has set their privacy to never we should immediately anonymize
331
                # If a borrower has set their privacy to never we should immediately anonymize
326
                # their checkouts
332
                # their checkouts
327
                if( $self->privacy() == 2 && $self_from_storage->privacy() != 2 ){
333
                if ( $self->privacy() == 2 && $self_from_storage->privacy() != 2 ) {
328
                    try{
334
                    try {
329
                        $self->old_checkouts->anonymize;
335
                        $self->old_checkouts->anonymize;
330
                    }
336
                    } catch {
331
                    catch {
337
                        Koha::Exceptions::Patron::FailedAnonymizing->throw( error => @_ );
332
                        Koha::Exceptions::Patron::FailedAnonymizing->throw(
333
                            error => @_
334
                        );
335
                    };
338
                    };
336
                }
339
                }
337
340
338
                # Password must be updated using $self->set_password
341
                # Password must be updated using $self->set_password
339
                $self->password($self_from_storage->password);
342
                $self->password( $self_from_storage->password );
343
344
                if ( $self->category->categorycode ne $self_from_storage->category->categorycode ) {
340
345
341
                if ( $self->category->categorycode ne
342
                    $self_from_storage->category->categorycode )
343
                {
344
                    # Add enrolement fee on category change if required
346
                    # Add enrolement fee on category change if required
345
                    $self->add_enrolment_fee_if_needed(1)
347
                    $self->add_enrolment_fee_if_needed(1)
346
                      if C4::Context->preference('FeeOnChangePatronCategory');
348
                        if C4::Context->preference('FeeOnChangePatronCategory');
347
349
348
                    # Clean up guarantors on category change if required
350
                    # Clean up guarantors on category change if required
349
                    $self->guarantor_relationships->delete
351
                    $self->guarantor_relationships->delete
350
                      unless ( $self->category->can_be_guarantee );
352
                        unless ( $self->category->can_be_guarantee );
351
353
352
                }
354
                }
353
355
354
                my @existing_guarantors = $self->guarantor_relationships()->guarantors->as_list;
356
                my @existing_guarantors = $self->guarantor_relationships()->guarantors->as_list;
355
                push @$guarantors, @existing_guarantors;
357
                push @$guarantors, @existing_guarantors;
356
358
357
                if ( C4::Context->preference('ChildNeedsGuarantor')
359
                if (    C4::Context->preference('ChildNeedsGuarantor')
358
                    and ( $self->is_child or $self->category->can_be_guarantee )
360
                    and ( $self->is_child or $self->category->can_be_guarantee )
359
                    and $self->contactname eq ""
361
                    and $self->contactname eq ""
360
                    and !@$guarantors )
362
                    and !@$guarantors )
Lines 363-371 sub store { Link Here
363
                }
365
                }
364
366
365
                foreach my $guarantor (@$guarantors) {
367
                foreach my $guarantor (@$guarantors) {
366
                    if ( $guarantor->is_child or $guarantor->category->can_be_guarantee) {
368
                    if ( $guarantor->is_child or $guarantor->category->can_be_guarantee ) {
367
                        Koha::Exceptions::Patron::Relationship::InvalidRelationship
369
                        Koha::Exceptions::Patron::Relationship::InvalidRelationship->throw( invalid_guarantor => 1 );
368
                        ->throw( invalid_guarantor => 1 );
369
                    }
370
                    }
370
                }
371
                }
371
372
Lines 378-396 sub store { Link Here
378
                    for my $key ( keys %{$from_storage} ) {
379
                    for my $key ( keys %{$from_storage} ) {
379
                        next if any { /$key/ } @skip_fields;
380
                        next if any { /$key/ } @skip_fields;
380
                        if (
381
                        if (
381
                            (
382
                            ( !defined( $from_storage->{$key} ) && defined( $from_object->{$key} ) )
382
                                  !defined( $from_storage->{$key} )
383
                                && defined( $from_object->{$key} )
384
                            )
385
                            || ( defined( $from_storage->{$key} )
383
                            || ( defined( $from_storage->{$key} )
386
                                && !defined( $from_object->{$key} ) )
384
                                && !defined( $from_object->{$key} ) )
387
                            || (
385
                            || (   defined( $from_storage->{$key} )
388
                                   defined( $from_storage->{$key} )
389
                                && defined( $from_object->{$key} )
386
                                && defined( $from_object->{$key} )
390
                                && ( $from_storage->{$key} ne
387
                                && ( $from_storage->{$key} ne $from_object->{$key} ) )
391
                                    $from_object->{$key} )
392
                            )
388
                            )
393
                          )
394
                        {
389
                        {
395
                            $info->{$key} = {
390
                            $info->{$key} = {
396
                                before => $from_storage->{$key},
391
                                before => $from_storage->{$key},
(-)a/installer/data/mysql/atomicupdate/bug_12133.pl (-5 / +7 lines)
Lines 1-16 Link Here
1
use Modern::Perl;
1
use Modern::Perl;
2
2
3
return {
3
return {
4
    bug_number => "12133",
4
    bug_number  => "12133",
5
    description => "Add system preference ChildNeedsGuarantor",
5
    description => "Add system preference ChildNeedsGuarantor",
6
    up => sub {
6
    up          => sub {
7
        my ($args) = @_;
7
        my ($args) = @_;
8
        my ($dbh, $out) = @$args{qw(dbh out)};
8
        my ( $dbh, $out ) = @$args{qw(dbh out)};
9
9
10
        $dbh->do( q{
10
        $dbh->do(
11
            q{
11
            INSERT IGNORE INTO systempreferences (variable,value,explanation,options,type)
12
            INSERT IGNORE INTO systempreferences (variable,value,explanation,options,type)
12
            VALUES('ChildNeedsGuarantor', 0, 'If ON, a child patron must have a guarantor when adding the patron.', '', 'YesNo');
13
            VALUES('ChildNeedsGuarantor', 0, 'If ON, a child patron must have a guarantor when adding the patron.', '', 'YesNo');
13
        } );
14
        }
15
        );
14
16
15
        say $out "Added new system preference 'ChildNeedsGuarantor')\n";
17
        say $out "Added new system preference 'ChildNeedsGuarantor')\n";
16
    },
18
    },
(-)a/members/memberentry.pl (-7 / +8 lines)
Lines 261-273 if ( ( $op eq 'insert' ) and !$nodouble ) { Link Here
261
261
262
#Attempt to delete guarantors
262
#Attempt to delete guarantors
263
my @delete_guarantor = $input->multi_param('delete_guarantor');
263
my @delete_guarantor = $input->multi_param('delete_guarantor');
264
if(@delete_guarantor){
264
if (@delete_guarantor) {
265
    if ( C4::Context->preference('ChildNeedsGuarantor')
265
    if ( C4::Context->preference('ChildNeedsGuarantor')
266
        && scalar @guarantors - scalar @delete_guarantor == 0 ) {
266
        && scalar @guarantors - scalar @delete_guarantor == 0 )
267
    {
267
        push @errors, 'ERROR_cannot_delete_guarantor';
268
        push @errors, 'ERROR_cannot_delete_guarantor';
268
    } else {
269
    } else {
269
        foreach my $id ( @delete_guarantor ) {
270
        foreach my $id (@delete_guarantor) {
270
            my $r = Koha::Patron::Relationships->find( $id );
271
            my $r = Koha::Patron::Relationships->find($id);
271
            $r->delete() if $r;
272
            $r->delete() if $r;
272
        }
273
        }
273
    }
274
    }
Lines 275-281 if(@delete_guarantor){ Link Here
275
276
276
#Check if guarantor requirements are met
277
#Check if guarantor requirements are met
277
my $valid_guarantor = @guarantors ? @guarantors : $newdata{'contactname'};
278
my $valid_guarantor = @guarantors ? @guarantors : $newdata{'contactname'};
278
if ( ( $op eq 'save' || $op eq 'insert' )
279
if (   ( $op eq 'save' || $op eq 'insert' )
279
    && C4::Context->preference('ChildNeedsGuarantor')
280
    && C4::Context->preference('ChildNeedsGuarantor')
280
    && ( $category->category_type eq 'C' || $category->can_be_guarantee )
281
    && ( $category->category_type eq 'C' || $category->can_be_guarantee )
281
    && !$valid_guarantor )
282
    && !$valid_guarantor )
Lines 427-433 if ((!$nok) and $nodouble and ($op eq 'insert' or $op eq 'save')){ Link Here
427
        delete $newdata{password2};
428
        delete $newdata{password2};
428
        $success = 1;
429
        $success = 1;
429
        $patron = try {
430
        $patron = try {
430
            Koha::Patron->new(\%newdata)->store({ guarantors => \@guarantors });
431
            Koha::Patron->new( \%newdata )->store( { guarantors => \@guarantors } );
431
        } catch {
432
        } catch {
432
            $success = 0;
433
            $success = 0;
433
            $nok = 1;
434
            $nok = 1;
Lines 521-527 if ((!$nok) and $nodouble and ($op eq 'insert' or $op eq 'save')){ Link Here
521
        delete $newdata{password2};
522
        delete $newdata{password2};
522
523
523
        try {
524
        try {
524
            $patron->set(\%newdata)->store({ guarantors => \@guarantors }) if scalar(keys %newdata) > 1;
525
            $patron->set( \%newdata )->store( { guarantors => \@guarantors } ) if scalar( keys %newdata ) > 1;
525
                # bug 4508 - avoid crash if we're not updating any columns in the borrowers table (editing patron attrs or msg prefs)
526
                # bug 4508 - avoid crash if we're not updating any columns in the borrowers table (editing patron attrs or msg prefs)
526
            $success = 1;
527
            $success = 1;
527
        } catch {
528
        } catch {
(-)a/t/db_dependent/Koha/Patron.t (-15 / +15 lines)
Lines 2153-2164 subtest 'guarantor requirements tests' => sub { Link Here
2153
    $schema->storage->txn_begin;
2153
    $schema->storage->txn_begin;
2154
2154
2155
    my $branchcode = $builder->build( { source => 'Branch' } )->{branchcode};
2155
    my $branchcode = $builder->build( { source => 'Branch' } )->{branchcode};
2156
    my $child_category = $builder->build(
2156
    my $child_category =
2157
        { source => 'Category', value => { category_type => 'C', can_be_guarantee => 1 } } )
2157
        $builder->build( { source => 'Category', value => { category_type => 'C', can_be_guarantee => 1 } } )
2158
      ->{categorycode};
2158
        ->{categorycode};
2159
    my $patron_category = $builder->build(
2159
    my $patron_category =
2160
        { source => 'Category', value => { category_type => 'A', can_be_guarantee => 0 } } )
2160
        $builder->build( { source => 'Category', value => { category_type => 'A', can_be_guarantee => 0 } } )
2161
      ->{categorycode};
2161
        ->{categorycode};
2162
2162
2163
    t::lib::Mocks::mock_preference( 'ChildNeedsGuarantor', 0 );
2163
    t::lib::Mocks::mock_preference( 'ChildNeedsGuarantor', 0 );
2164
2164
Lines 2200-2235 subtest 'guarantor requirements tests' => sub { Link Here
2200
2200
2201
    throws_ok { $child2->store(); }
2201
    throws_ok { $child2->store(); }
2202
    'Koha::Exceptions::Patron::Relationship::NoGuarantor',
2202
    'Koha::Exceptions::Patron::Relationship::NoGuarantor',
2203
      'Exception thrown when guarantor is required but not provided.';
2203
        'Exception thrown when guarantor is required but not provided.';
2204
2204
2205
    my @guarantors = ( $patron, $child3 );
2205
    my @guarantors = ( $patron, $child3 );
2206
    throws_ok { $child2->store( { guarantors => \@guarantors } ); }
2206
    throws_ok { $child2->store( { guarantors => \@guarantors } ); }
2207
    'Koha::Exceptions::Patron::Relationship::InvalidRelationship',
2207
    'Koha::Exceptions::Patron::Relationship::InvalidRelationship',
2208
      'Exception thrown when child patron is added as guarantor.';
2208
        'Exception thrown when child patron is added as guarantor.';
2209
2209
2210
    #test ModMember
2210
    #test ModMember
2211
    @guarantors = ( $patron );
2211
    @guarantors = ($patron);
2212
    $child2->store( { guarantors => \@guarantors } )->discard_changes();
2212
    $child2->store( { guarantors => \@guarantors } )->discard_changes();
2213
2213
2214
    t::lib::Mocks::mock_preference( 'borrowerRelationship', '' );
2214
    t::lib::Mocks::mock_preference( 'borrowerRelationship', '' );
2215
2215
2216
    my $relationship = Koha::Patron::Relationship->new(
2216
    my $relationship = Koha::Patron::Relationship->new(
2217
        {   guarantor_id => $patron->borrowernumber,
2217
        {
2218
            guarantor_id => $patron->borrowernumber,
2218
            guarantee_id => $child2->borrowernumber,
2219
            guarantee_id => $child2->borrowernumber,
2219
            relationship => ''
2220
            relationship => ''
2220
        }
2221
        }
2221
    );
2222
    );
2222
    $relationship->store();
2223
    $relationship->store();
2223
2224
2224
    ok( $child2->store(), 'Child patron can be modified and stored when guarantor is stored');
2225
    ok( $child2->store(), 'Child patron can be modified and stored when guarantor is stored' );
2225
2226
2226
    @guarantors = ( $child3 );
2227
    @guarantors = ($child3);
2227
    throws_ok { $child2->store( { guarantors => \@guarantors } ); }
2228
    throws_ok { $child2->store( { guarantors => \@guarantors } ); }
2228
    'Koha::Exceptions::Patron::Relationship::InvalidRelationship',
2229
    'Koha::Exceptions::Patron::Relationship::InvalidRelationship',
2229
      'Exception thrown when child patron is modified and child patron is added as guarantor.';
2230
        'Exception thrown when child patron is modified and child patron is added as guarantor.';
2230
2231
2231
    $relationship->delete;
2232
    $relationship->delete;
2232
    throws_ok { $child2->store(); }
2233
    throws_ok { $child2->store(); }
2233
    'Koha::Exceptions::Patron::Relationship::NoGuarantor',
2234
    'Koha::Exceptions::Patron::Relationship::NoGuarantor',
2234
      'Exception thrown when guarantor is deleted.';
2235
        'Exception thrown when guarantor is deleted.';
2235
};
2236
};
2236
- 

Return to bug 12133