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

(-)a/C4/Circulation.pm (-9 / +15 lines)
Lines 2727-2733 sub GetUpcomingDueIssues { Link Here
2727
2727
2728
=head2 CanBookBeRenewed
2728
=head2 CanBookBeRenewed
2729
2729
2730
  ($ok,$error) = &CanBookBeRenewed($borrowernumber, $itemnumber[, $override_limit]);
2730
  ($ok,$error,$info) = &CanBookBeRenewed($borrowernumber, $itemnumber[, $override_limit]);
2731
2731
2732
Find out whether a borrowed item may be renewed.
2732
Find out whether a borrowed item may be renewed.
2733
2733
Lines 2745-2751 that are automatically renewed. Link Here
2745
C<$CanBookBeRenewed> returns a true value if the item may be renewed. The
2745
C<$CanBookBeRenewed> returns a true value if the item may be renewed. The
2746
item must currently be on loan to the specified borrower; renewals
2746
item must currently be on loan to the specified borrower; renewals
2747
must be allowed for the item's type; and the borrower must not have
2747
must be allowed for the item's type; and the borrower must not have
2748
already renewed the loan. $error will contain the reason the renewal can not proceed
2748
already renewed the loan.
2749
    $error will contain the reason the renewal can not proceed
2750
    $info will contain a hash of additional info
2751
      currently 'soonest_renew_date' if error is 'too soon'
2749
2752
2750
=cut
2753
=cut
2751
2754
Lines 2753-2758 sub CanBookBeRenewed { Link Here
2753
    my ( $borrowernumber, $itemnumber, $override_limit, $cron ) = @_;
2756
    my ( $borrowernumber, $itemnumber, $override_limit, $cron ) = @_;
2754
2757
2755
    my $auto_renew = "no";
2758
    my $auto_renew = "no";
2759
    my $soonest;
2756
2760
2757
    my $item      = Koha::Items->find($itemnumber)      or return ( 0, 'no_item' );
2761
    my $item      = Koha::Items->find($itemnumber)      or return ( 0, 'no_item' );
2758
    my $issue = $item->checkout or return ( 0, 'no_checkout' );
2762
    my $issue = $item->checkout or return ( 0, 'no_checkout' );
Lines 2797-2809 sub CanBookBeRenewed { Link Here
2797
            return ( 0, 'overdue');
2801
            return ( 0, 'overdue');
2798
        }
2802
        }
2799
2803
2800
        $auto_renew = _CanBookBeAutoRenewed({
2804
        ( $auto_renew, $soonest ) = _CanBookBeAutoRenewed({
2801
            patron     => $patron,
2805
            patron     => $patron,
2802
            item       => $item,
2806
            item       => $item,
2803
            branchcode => $branchcode,
2807
            branchcode => $branchcode,
2804
            issue      => $issue
2808
            issue      => $issue
2805
        });
2809
        });
2806
        return ( 0, $auto_renew  ) if $auto_renew =~ 'auto_too_soon' && $cron;
2810
        return ( 0, $auto_renew, { soonest_renew_date => $soonest } ) if $auto_renew =~ 'auto_too_soon' && $cron;
2807
        # cron wants 'too_soon' over 'on_reserve' for performance and to avoid
2811
        # cron wants 'too_soon' over 'on_reserve' for performance and to avoid
2808
        # extra notices being sent. Cron also implies no override
2812
        # extra notices being sent. Cron also implies no override
2809
        return ( 0, $auto_renew  ) if $auto_renew =~ 'auto_account_expired';
2813
        return ( 0, $auto_renew  ) if $auto_renew =~ 'auto_account_expired';
Lines 2890-2898 sub CanBookBeRenewed { Link Here
2890
    }
2894
    }
2891
2895
2892
    return ( 0, "on_reserve" ) if $resfound;    # '' when no hold was found
2896
    return ( 0, "on_reserve" ) if $resfound;    # '' when no hold was found
2893
    return ( 0, $auto_renew  ) if $auto_renew =~ 'too_soon';#$auto_renew ne "no" && $auto_renew ne "ok";
2897
    return ( 0, $auto_renew, { soonest_renew_date => $soonest } ) if $auto_renew =~ 'too_soon';#$auto_renew ne "no" && $auto_renew ne "ok";
2894
    if ( GetSoonestRenewDate($borrowernumber, $itemnumber) > dt_from_string() ){
2898
    $soonest = GetSoonestRenewDate($borrowernumber, $itemnumber);
2895
        return (0, "too_soon") unless $override_limit;
2899
    if ( $soonest > dt_from_string() ){
2900
        return (0, "too_soon", { soonest_renew_date => $soonest } ) unless $override_limit;
2896
    }
2901
    }
2897
2902
2898
    return ( 0, "auto_renew" ) if $auto_renew eq "ok" && !$override_limit; # 0 if auto-renewal should not succeed
2903
    return ( 0, "auto_renew" ) if $auto_renew eq "ok" && !$override_limit; # 0 if auto-renewal should not succeed
Lines 4350-4358 sub _CanBookBeAutoRenewed { Link Here
4350
        }
4355
        }
4351
    }
4356
    }
4352
4357
4353
    if ( GetSoonestRenewDate( $patron->id, $item->id ) > dt_from_string() )
4358
    my $soonest = GetSoonestRenewDate($patron->id, $item->id);
4359
    if ( $soonest > dt_from_string() )
4354
    {
4360
    {
4355
        return "auto_too_soon";
4361
        return ( "auto_too_soon", $soonest );
4356
    }
4362
    }
4357
4363
4358
    return "ok";
4364
    return "ok";
(-)a/circ/renew.pl (-6 / +4 lines)
Lines 23-29 use CGI qw ( -utf8 ); Link Here
23
use C4::Context;
23
use C4::Context;
24
use C4::Auth qw( get_template_and_user );
24
use C4::Auth qw( get_template_and_user );
25
use C4::Output qw( output_html_with_http_headers );
25
use C4::Output qw( output_html_with_http_headers );
26
use C4::Circulation qw( barcodedecode CanBookBeRenewed GetSoonestRenewDate GetLatestAutoRenewDate AddRenewal );
26
use C4::Circulation qw( barcodedecode CanBookBeRenewed GetLatestAutoRenewDate AddRenewal );
27
use Koha::DateUtils qw( dt_from_string output_pref );
27
use Koha::DateUtils qw( dt_from_string output_pref );
28
use Koha::Database;
28
use Koha::Database;
29
use Koha::BiblioFrameworks;
29
use Koha::BiblioFrameworks;
Lines 67-73 if ($barcode) { Link Here
67
            
67
            
68
            if ( ( $borrower->debarred() || q{} ) lt dt_from_string()->ymd() ) {
68
            if ( ( $borrower->debarred() || q{} ) lt dt_from_string()->ymd() ) {
69
                my $can_renew;
69
                my $can_renew;
70
                ( $can_renew, $error ) =
70
                my $info;
71
                ( $can_renew, $error, $info ) =
71
                  CanBookBeRenewed( $borrower->borrowernumber(),
72
                  CanBookBeRenewed( $borrower->borrowernumber(),
72
                    $item->itemnumber(), $override_limit );
73
                    $item->itemnumber(), $override_limit );
73
74
Lines 82-91 if ($barcode) { Link Here
82
                }
83
                }
83
84
84
                if ( $error && ($error eq 'too_soon' or $error eq 'auto_too_soon') ) {
85
                if ( $error && ($error eq 'too_soon' or $error eq 'auto_too_soon') ) {
85
                    $soonest_renew_date = C4::Circulation::GetSoonestRenewDate(
86
                    $soonest_renew_date = $info->{soonest_renew_date};
86
                        $borrower->borrowernumber(),
87
                        $item->itemnumber(),
88
                    );
89
                }
87
                }
90
                if ( $error && ( $error eq 'auto_too_late' ) ) {
88
                if ( $error && ( $error eq 'auto_too_late' ) ) {
91
                    $latest_auto_renew_date = C4::Circulation::GetLatestAutoRenewDate(
89
                    $latest_auto_renew_date = C4::Circulation::GetLatestAutoRenewDate(
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-user.tt (-1 / +1 lines)
Lines 404-410 Link Here
404
                                                                [% END %]
404
                                                                [% END %]
405
                                                            )</span>
405
                                                            )</span>
406
                                                        [% ELSIF ( ISSUE.too_soon ) %]
406
                                                        [% ELSIF ( ISSUE.too_soon ) %]
407
                                                            No renewal before [% ISSUE.soonestrenewdate | html %]
407
                                                            No renewal before [% ISSUE.soonestrenewdate | $KohaDates as_due_date => 1 %]
408
                                                            <span class="renewals">(
408
                                                            <span class="renewals">(
409
                                                                [% ISSUE.renewsleft | html %] of [% ISSUE.renewsallowed | html %] renewals remaining
409
                                                                [% ISSUE.renewsleft | html %] of [% ISSUE.renewsallowed | html %] renewals remaining
410
                                                                [% IF Koha.Preference('UnseenRenewals') && ISSUE.unseenallowed %]
410
                                                                [% IF Koha.Preference('UnseenRenewals') && ISSUE.unseenallowed %]
(-)a/opac/opac-user.pl (-8 / +3 lines)
Lines 27-33 use C4::Koha qw( Link Here
27
    GetNormalizedISBN
27
    GetNormalizedISBN
28
    GetNormalizedUPC
28
    GetNormalizedUPC
29
);
29
);
30
use C4::Circulation qw( CanBookBeRenewed GetRenewCount GetIssuingCharges GetSoonestRenewDate );
30
use C4::Circulation qw( CanBookBeRenewed GetRenewCount GetIssuingCharges );
31
use C4::External::BakerTaylor qw( image_url link_url );
31
use C4::External::BakerTaylor qw( image_url link_url );
32
use C4::Reserves qw( GetReserveStatus );
32
use C4::Reserves qw( GetReserveStatus );
33
use C4::Members;
33
use C4::Members;
Lines 222-228 if ( $pending_checkouts->count ) { # Useless test Link Here
222
        $issue->{rentalfines} = $rental_fines->total_outstanding;
222
        $issue->{rentalfines} = $rental_fines->total_outstanding;
223
223
224
        # check if item is renewable
224
        # check if item is renewable
225
        my ($status,$renewerror) = CanBookBeRenewed( $borrowernumber, $issue->{'itemnumber'} );
225
        my ($status,$renewerror,$info) = CanBookBeRenewed( $borrowernumber, $issue->{'itemnumber'} );
226
        (
226
        (
227
            $issue->{'renewcount'},
227
            $issue->{'renewcount'},
228
            $issue->{'renewsallowed'},
228
            $issue->{'renewsallowed'},
Lines 253-264 if ( $pending_checkouts->count ) { # Useless test Link Here
253
253
254
            if ( $renewerror eq 'too_soon' ) {
254
            if ( $renewerror eq 'too_soon' ) {
255
                $issue->{'too_soon'}         = 1;
255
                $issue->{'too_soon'}         = 1;
256
                $issue->{'soonestrenewdate'} = output_pref(
256
                $issue->{'soonestrenewdate'} = $info->{soonest_renew_date};
257
                    C4::Circulation::GetSoonestRenewDate(
258
                        $issue->{borrowernumber},
259
                        $issue->{itemnumber}
260
                    )
261
                );
262
            }
257
            }
263
        }
258
        }
264
259
(-)a/svc/checkouts (-3 / +3 lines)
Lines 23-29 use CGI; Link Here
23
use JSON qw(to_json);
23
use JSON qw(to_json);
24
24
25
use C4::Auth qw(check_cookie_auth haspermission);
25
use C4::Auth qw(check_cookie_auth haspermission);
26
use C4::Circulation qw(GetIssuingCharges CanBookBeRenewed GetRenewCount GetSoonestRenewDate);
26
use C4::Circulation qw(GetIssuingCharges CanBookBeRenewed GetRenewCount );
27
use C4::Overdues qw(GetFine);
27
use C4::Overdues qw(GetFine);
28
use C4::Context;
28
use C4::Context;
29
29
Lines 153-165 while ( my $c = $sth->fetchrow_hashref() ) { Link Here
153
    my ($charge) = GetIssuingCharges( $c->{itemnumber}, $c->{borrowernumber} );
153
    my ($charge) = GetIssuingCharges( $c->{itemnumber}, $c->{borrowernumber} );
154
    my $fine = GetFine( $c->{itemnumber}, $c->{borrowernumber} );
154
    my $fine = GetFine( $c->{itemnumber}, $c->{borrowernumber} );
155
155
156
    my ( $can_renew, $can_renew_error ) =
156
    my ( $can_renew, $can_renew_error, $info ) =
157
      CanBookBeRenewed( $c->{borrowernumber}, $c->{itemnumber} );
157
      CanBookBeRenewed( $c->{borrowernumber}, $c->{itemnumber} );
158
    my $can_renew_date =
158
    my $can_renew_date =
159
      $can_renew_error && $can_renew_error eq 'too_soon'
159
      $can_renew_error && $can_renew_error eq 'too_soon'
160
      ? output_pref(
160
      ? output_pref(
161
        {
161
        {
162
            dt => GetSoonestRenewDate( $c->{borrowernumber}, $c->{itemnumber} ),
162
            dt => $info->{soonest_renew_date},
163
            as_due_date => 1
163
            as_due_date => 1
164
        }
164
        }
165
      )
165
      )
(-)a/t/db_dependent/Circulation.t (-9 / +16 lines)
Lines 418-424 subtest "GetIssuingCharges tests" => sub { Link Here
418
418
419
my ( $reused_itemnumber_1, $reused_itemnumber_2 );
419
my ( $reused_itemnumber_1, $reused_itemnumber_2 );
420
subtest "CanBookBeRenewed tests" => sub {
420
subtest "CanBookBeRenewed tests" => sub {
421
    plan tests => 93;
421
    plan tests => 100;
422
422
423
    C4::Context->set_preference('ItemsDeniedRenewal','');
423
    C4::Context->set_preference('ItemsDeniedRenewal','');
424
    # Generate test biblio
424
    # Generate test biblio
Lines 794-804 subtest "CanBookBeRenewed tests" => sub { Link Here
794
    );
794
    );
795
795
796
    $issue = AddIssue( $renewing_borrower, $item_4->barcode, undef, undef, undef, undef, { auto_renew => 1 } );
796
    $issue = AddIssue( $renewing_borrower, $item_4->barcode, undef, undef, undef, undef, { auto_renew => 1 } );
797
    ( $renewokay, $error ) =
797
    my $info;
798
    ( $renewokay, $error, $info ) =
798
      CanBookBeRenewed( $renewing_borrowernumber, $item_4->itemnumber );
799
      CanBookBeRenewed( $renewing_borrowernumber, $item_4->itemnumber );
799
    is( $renewokay, 0, 'Bug 14101: Cannot renew, renewal is automatic and premature' );
800
    is( $renewokay, 0, 'Bug 14101: Cannot renew, renewal is automatic and premature' );
800
    is( $error, 'auto_too_soon',
801
    is( $error, 'auto_too_soon',
801
        'Bug 14101: Cannot renew, renewal is automatic and premature, "No renewal before" = undef (returned code is auto_too_soon)' );
802
        'Bug 14101: Cannot renew, renewal is automatic and premature, "No renewal before" = undef (returned code is auto_too_soon)' );
803
    is( $info->{soonest_renew_date} , dt_from_string($issue->date_due), "Due date is returned as earliest renewal date when error is 'auto_too_soon'" );
802
    AddReserve(
804
    AddReserve(
803
        {
805
        {
804
            branchcode       => $branch,
806
            branchcode       => $branch,
Lines 817-825 subtest "CanBookBeRenewed tests" => sub { Link Here
817
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $item_4->itemnumber );
819
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $item_4->itemnumber );
818
    is( $renewokay, 0, 'Still should not be able to renew' );
820
    is( $renewokay, 0, 'Still should not be able to renew' );
819
    is( $error, 'on_reserve', 'returned code is on_reserve, reserve checked when not checking for cron' );
821
    is( $error, 'on_reserve', 'returned code is on_reserve, reserve checked when not checking for cron' );
820
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $item_4->itemnumber, undef, 1 );
822
    ( $renewokay, $error, $info ) = CanBookBeRenewed( $renewing_borrowernumber, $item_4->itemnumber, undef, 1 );
821
    is( $renewokay, 0, 'Still should not be able to renew' );
823
    is( $renewokay, 0, 'Still should not be able to renew' );
822
    is( $error, 'auto_too_soon', 'returned code is auto_too_soon, reserve not checked when checking for cron' );
824
    is( $error, 'auto_too_soon', 'returned code is auto_too_soon, reserve not checked when checking for cron' );
825
    is( $info->{soonest_renew_date}, dt_from_string($issue->date_due), "Due date is returned as earliest renewal date when error is 'auto_too_soon'" );
823
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $item_4->itemnumber, 1 );
826
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $item_4->itemnumber, 1 );
824
    is( $renewokay, 0, 'Still should not be able to renew' );
827
    is( $renewokay, 0, 'Still should not be able to renew' );
825
    is( $error, 'on_reserve', 'returned code is on_reserve, auto_too_soon limit is overridden' );
828
    is( $error, 'on_reserve', 'returned code is on_reserve, auto_too_soon limit is overridden' );
Lines 852-890 subtest "CanBookBeRenewed tests" => sub { Link Here
852
        }
855
        }
853
    );
856
    );
854
857
855
    ( $renewokay, $error ) = CanBookBeRenewed($renewing_borrowernumber, $item_1->itemnumber);
858
    ( $renewokay, $error, $info ) = CanBookBeRenewed($renewing_borrowernumber, $item_1->itemnumber);
856
    is( $renewokay, 0, 'Bug 7413: Cannot renew, renewal is premature');
859
    is( $renewokay, 0, 'Bug 7413: Cannot renew, renewal is premature');
857
    is( $error, 'too_soon', 'Bug 7413: Cannot renew, renewal is premature (returned code is too_soon)');
860
    is( $error, 'too_soon', 'Bug 7413: Cannot renew, renewal is premature (returned code is too_soon)');
861
    is( $info->{soonest_renew_date}, dt_from_string($issue->date_due)->subtract( days => 7 ), "Soonest renew date returned when error is 'too_soon'");
858
862
859
    # Bug 14101
863
    # Bug 14101
860
    # Test premature automatic renewal
864
    # Test premature automatic renewal
861
    ( $renewokay, $error ) =
865
    ( $renewokay, $error, $info ) =
862
      CanBookBeRenewed( $renewing_borrowernumber, $item_4->itemnumber );
866
      CanBookBeRenewed( $renewing_borrowernumber, $item_4->itemnumber );
863
    is( $renewokay, 0, 'Bug 14101: Cannot renew, renewal is automatic and premature' );
867
    is( $renewokay, 0, 'Bug 14101: Cannot renew, renewal is automatic and premature' );
864
    is( $error, 'auto_too_soon',
868
    is( $error, 'auto_too_soon',
865
        'Bug 14101: Cannot renew, renewal is automatic and premature (returned code is auto_too_soon)'
869
        'Bug 14101: Cannot renew, renewal is automatic and premature (returned code is auto_too_soon)'
866
    );
870
    );
871
    is( $info->{soonest_renew_date}, dt_from_string($issue->date_due)->subtract( days => 7 ), "Soonest renew date returned when error is 'auto_too_soon'");
867
872
868
    $renewing_borrower_obj->autorenew_checkouts(0)->store;
873
    $renewing_borrower_obj->autorenew_checkouts(0)->store;
869
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $item_4->itemnumber );
874
    ( $renewokay, $error, $info ) = CanBookBeRenewed( $renewing_borrowernumber, $item_4->itemnumber );
870
    is( $renewokay, 0, 'No renewal before is 7, patron opted out of auto_renewal still cannot renew early' );
875
    is( $renewokay, 0, 'No renewal before is 7, patron opted out of auto_renewal still cannot renew early' );
871
    is( $error, 'too_soon', 'Error is too_soon, no auto' );
876
    is( $error, 'too_soon', 'Error is too_soon, no auto' );
877
    is( $info->{soonest_renew_date}, dt_from_string($issue->date_due)->subtract( days => 7 ), "Soonest renew date returned when error is 'too_soon'");
872
    $renewing_borrower_obj->autorenew_checkouts(1)->store;
878
    $renewing_borrower_obj->autorenew_checkouts(1)->store;
873
879
874
    # Change policy so that loans can only be renewed exactly on due date (0 days prior to due date)
880
    # Change policy so that loans can only be renewed exactly on due date (0 days prior to due date)
875
    # and test automatic renewal again
881
    # and test automatic renewal again
876
    $dbh->do(q{UPDATE circulation_rules SET rule_value = '0' WHERE rule_name = 'norenewalbefore'});
882
    $dbh->do(q{UPDATE circulation_rules SET rule_value = '0' WHERE rule_name = 'norenewalbefore'});
877
    ( $renewokay, $error ) =
883
    ( $renewokay, $error, $info ) =
878
      CanBookBeRenewed( $renewing_borrowernumber, $item_4->itemnumber );
884
      CanBookBeRenewed( $renewing_borrowernumber, $item_4->itemnumber );
879
    is( $renewokay, 0, 'Bug 14101: Cannot renew, renewal is automatic and premature' );
885
    is( $renewokay, 0, 'Bug 14101: Cannot renew, renewal is automatic and premature' );
880
    is( $error, 'auto_too_soon',
886
    is( $error, 'auto_too_soon',
881
        'Bug 14101: Cannot renew, renewal is automatic and premature, "No renewal before" = 0 (returned code is auto_too_soon)'
887
        'Bug 14101: Cannot renew, renewal is automatic and premature, "No renewal before" = 0 (returned code is auto_too_soon)'
882
    );
888
    );
889
    is( $info->{soonest_renew_date}, dt_from_string($issue->date_due), "Soonest renew date returned when error is 'auto_too_soon'");
883
890
884
    $renewing_borrower_obj->autorenew_checkouts(0)->store;
891
    $renewing_borrower_obj->autorenew_checkouts(0)->store;
885
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $item_4->itemnumber );
892
    ( $renewokay, $error, $info ) = CanBookBeRenewed( $renewing_borrowernumber, $item_4->itemnumber );
886
    is( $renewokay, 0, 'No renewal before is 0, patron opted out of auto_renewal still cannot renew early' );
893
    is( $renewokay, 0, 'No renewal before is 0, patron opted out of auto_renewal still cannot renew early' );
887
    is( $error, 'too_soon', 'Error is too_soon, no auto' );
894
    is( $error, 'too_soon', 'Error is too_soon, no auto' );
895
    is( $info->{soonest_renew_date}, dt_from_string($issue->date_due), "Soonest renew date returned when error is 'auto_too_soon'");
888
    $renewing_borrower_obj->autorenew_checkouts(1)->store;
896
    $renewing_borrower_obj->autorenew_checkouts(1)->store;
889
897
890
    # Change policy so that loans can be renewed 99 days prior to the due date
898
    # Change policy so that loans can be renewed 99 days prior to the due date
891
- 

Return to bug 30167