@@ -, +, @@ --- C4/Circulation.pm | 172 +++++++++++++++++- C4/ILSDI/Services.pm | 2 +- C4/SIP/ILS/Transaction/Renew.pm | 1 + Koha/CirculationRules.pm | 3 + Koha/REST/V1/Checkouts.pm | 11 +- api/v1/swagger/definitions/checkout.json | 4 + api/v1/swagger/parameters.json | 3 + api/v1/swagger/parameters/checkout.json | 7 + api/v1/swagger/paths/checkouts.json | 7 +- circ/renew.pl | 11 +- .../prog/css/src/staff-global.scss | 12 ++ .../prog/en/includes/checkouts-table.inc | 4 + .../prog/en/modules/circ/circulation.tt | 1 + .../prog/en/modules/circ/renew.tt | 38 +++- .../prog/en/modules/members/moremember.tt | 1 + koha-tmpl/intranet-tmpl/prog/js/checkouts.js | 24 ++- .../bootstrap/en/modules/opac-user.tt | 44 ++++- misc/cronjobs/automatic_renewals.pl | 2 +- offline_circ/download.pl | 1 + opac/opac-renew.pl | 2 +- opac/opac-user.pl | 10 +- svc/checkouts | 12 +- svc/renew | 3 +- 23 files changed, 340 insertions(+), 35 deletions(-) --- a/C4/Circulation.pm +++ a/C4/Circulation.pm @@ -2671,6 +2671,7 @@ sub CanBookBeRenewed { 'no_auto_renewal_after_hard_limit', 'lengthunit', 'norenewalbefore', + 'unseen_renewals_allowed' ] } ); @@ -2849,12 +2850,124 @@ sub CanBookBeRenewed { return ( 0, "auto_renew" ) if $auto_renew eq "ok" && !$override_limit; # 0 if auto-renewal should not succeed + return ( 1, undef ) if $override_limit; + + my $branchcode = _GetCircControlBranch( $item->unblessed, $patron->unblessed ); + my $issuing_rule = Koha::CirculationRules->get_effective_rules( + { + categorycode => $patron->categorycode, + itemtype => $item->effective_itemtype, + branchcode => $branchcode, + rules => [ + 'renewalsallowed', + 'no_auto_renewal_after', + 'no_auto_renewal_after_hard_limit', + 'lengthunit', + 'norenewalbefore', + 'unseen_renewals_allowed' + ] + } + ); + + return ( 0, "too_many" ) + if not $issuing_rule->{renewalsallowed} or $issuing_rule->{renewalsallowed} <= $issue->renewals; + + return ( 0, "too_unseen" ) + if C4::Context->preference('UnseenRenewals') && + $issuing_rule->{unseen_renewals_allowed} && + $issuing_rule->{unseen_renewals_allowed} <= $issue->unseen_renewals; + + my $overduesblockrenewing = C4::Context->preference('OverduesBlockRenewing'); + my $restrictionblockrenewing = C4::Context->preference('RestrictionBlockRenewing'); + $patron = Koha::Patrons->find($borrowernumber); # FIXME Is this really useful? + my $restricted = $patron->is_debarred; + my $hasoverdues = $patron->has_overdues; + + if ( $restricted and $restrictionblockrenewing ) { + return ( 0, 'restriction'); + } elsif ( ($hasoverdues and $overduesblockrenewing eq 'block') || ($issue->is_overdue and $overduesblockrenewing eq 'blockitem') ) { + return ( 0, 'overdue'); + } + + if ( $issue->auto_renew ) { + + if ( $patron->category->effective_BlockExpiredPatronOpacActions and $patron->is_expired ) { + return ( 0, 'auto_account_expired' ); + } + + if ( defined $issuing_rule->{no_auto_renewal_after} + and $issuing_rule->{no_auto_renewal_after} ne "" ) { + # Get issue_date and add no_auto_renewal_after + # If this is greater than today, it's too late for renewal. + my $maximum_renewal_date = dt_from_string($issue->issuedate, 'sql'); + $maximum_renewal_date->add( + $issuing_rule->{lengthunit} => $issuing_rule->{no_auto_renewal_after} + ); + my $now = dt_from_string; + if ( $now >= $maximum_renewal_date ) { + return ( 0, "auto_too_late" ); + } + } + if ( defined $issuing_rule->{no_auto_renewal_after_hard_limit} + and $issuing_rule->{no_auto_renewal_after_hard_limit} ne "" ) { + # If no_auto_renewal_after_hard_limit is >= today, it's also too late for renewal + if ( dt_from_string >= dt_from_string( $issuing_rule->{no_auto_renewal_after_hard_limit} ) ) { + return ( 0, "auto_too_late" ); + } + } + + if ( C4::Context->preference('OPACFineNoRenewalsBlockAutoRenew') ) { + my $fine_no_renewals = C4::Context->preference("OPACFineNoRenewals"); + my $amountoutstanding = + C4::Context->preference("OPACFineNoRenewalsIncludeCredit") + ? $patron->account->balance + : $patron->account->outstanding_debits->total_outstanding; + if ( $amountoutstanding and $amountoutstanding > $fine_no_renewals ) { + return ( 0, "auto_too_much_oweing" ); + } + } + } + + if ( defined $issuing_rule->{norenewalbefore} + and $issuing_rule->{norenewalbefore} ne "" ) + { + + # Calculate soonest renewal by subtracting 'No renewal before' from due date + my $soonestrenewal = dt_from_string( $issue->date_due, 'sql' )->subtract( + $issuing_rule->{lengthunit} => $issuing_rule->{norenewalbefore} ); + + # Depending on syspref reset the exact time, only check the date + if ( C4::Context->preference('NoRenewalBeforePrecision') eq 'date' + and $issuing_rule->{lengthunit} eq 'days' ) + { + $soonestrenewal->truncate( to => 'day' ); + } + + if ( $soonestrenewal > DateTime->now( time_zone => C4::Context->tz() ) ) + { + return ( 0, "auto_too_soon" ) if $issue->auto_renew; + return ( 0, "too_soon" ); + } + elsif ( $issue->auto_renew ) { + return ( 0, "auto_renew" ); + } + } + + # Fallback for automatic renewals: + # If norenewalbefore is undef, don't renew before due date. + if ( $issue->auto_renew ) { + my $now = dt_from_string; + return ( 0, "auto_renew" ) + if $now >= dt_from_string( $issue->date_due, 'sql' ); + return ( 0, "auto_too_soon" ); + } + return ( 1, undef ); } =head2 AddRenewal - &AddRenewal($borrowernumber, $itemnumber, $branch, [$datedue], [$lastreneweddate]); + &AddRenewal($borrowernumber, $itemnumber, $branch, [$datedue], [$lastreneweddate], [$seen]); Renews a loan. @@ -2879,6 +2992,10 @@ syspref) If C<$datedue> is the empty string, C<&AddRenewal> will calculate the due date automatically from the book's item type. +C<$seen> is a boolean flag indicating if the item was seen or not during the renewal. This +informs the incrementing of the unseen_renewals column. If this flag is not supplied, we +fallback to a true value + =cut sub AddRenewal { @@ -2888,6 +3005,10 @@ sub AddRenewal { my $datedue = shift; my $lastreneweddate = shift || dt_from_string(); my $skipfinecalc = shift; + my $seen = shift; + + # Fallback on a 'seen' renewal + $seen = defined $seen && $seen == 0 ? 0 : 1; my $item_object = Koha::Items->find($itemnumber) or return; my $biblio = $item_object->biblio; @@ -2940,15 +3061,35 @@ sub AddRenewal { } ); + # Increment the unseen renewals, if appropriate + # We only do so if the syspref is enabled and + # a maximum value has been set in the circ rules + my $unseen_renewals = $issue->unseen_renewals; + if (C4::Context->preference('UnseenRenewals')) { + my $rule = Koha::CirculationRules->get_effective_rule( + { categorycode => $patron->categorycode, + itemtype => $item_object->effective_itemtype, + branchcode => $circ_library->branchcode, + rule_name => 'unseen_renewals_allowed' + } + ); + if (!$seen && $rule && $rule->rule_value) { + $unseen_renewals++; + } else { + # If the renewal is seen, unseen should revert to 0 + $unseen_renewals = 0; + } + } + # Update the issues record to have the new due date, and a new count # of how many times it has been renewed. my $renews = ( $issue->renewals || 0 ) + 1; - my $sth = $dbh->prepare("UPDATE issues SET date_due = ?, renewals = ?, lastreneweddate = ? + my $sth = $dbh->prepare("UPDATE issues SET date_due = ?, renewals = ?, unseen_renewals = ?, lastreneweddate = ? WHERE borrowernumber=? AND itemnumber=?" ); - $sth->execute( $datedue->strftime('%Y-%m-%d %H:%M'), $renews, $lastreneweddate, $borrowernumber, $itemnumber ); + $sth->execute( $datedue->strftime('%Y-%m-%d %H:%M'), $renews, $unseen_renewals, $lastreneweddate, $borrowernumber, $itemnumber ); # Update the renewal count on the item, and tell zebra to reindex $renews = ( $item_object->renewals || 0 ) + 1; @@ -3035,8 +3176,11 @@ sub GetRenewCount { my ( $bornum, $itemno ) = @_; my $dbh = C4::Context->dbh; my $renewcount = 0; + my $unseencount = 0; my $renewsallowed = 0; + my $unseenallowed = 0; my $renewsleft = 0; + my $unseenleft = 0; my $patron = Koha::Patrons->find( $bornum ); my $item = Koha::Items->find($itemno); @@ -3055,22 +3199,34 @@ sub GetRenewCount { $sth->execute( $bornum, $itemno ); my $data = $sth->fetchrow_hashref; $renewcount = $data->{'renewals'} if $data->{'renewals'}; + $unseencount = $data->{'unseen_renewals'} if $data->{'unseen_renewals'}; # $item and $borrower should be calculated my $branchcode = _GetCircControlBranch($item->unblessed, $patron->unblessed); - my $rule = Koha::CirculationRules->get_effective_rule( + my $rules = Koha::CirculationRules->get_effective_rules( { categorycode => $patron->categorycode, itemtype => $item->effective_itemtype, branchcode => $branchcode, - rule_name => 'renewalsallowed', + rules => [ 'renewalsallowed', 'unseen_renewals_allowed' ] } ); - - $renewsallowed = $rule ? $rule->rule_value : 0; + $renewsallowed = $rules ? $rules->{renewalsallowed} : 0; + $unseenallowed = $rules->{unseen_renewals_allowed} ? + $rules->{unseen_renewals_allowed} : + 0; $renewsleft = $renewsallowed - $renewcount; + $unseenleft = $unseenallowed - $unseencount; if($renewsleft < 0){ $renewsleft = 0; } - return ( $renewcount, $renewsallowed, $renewsleft ); + if($unseenleft < 0){ $unseenleft = 0; } + return ( + $renewcount, + $renewsallowed, + $renewsleft, + $unseencount, + $unseenallowed, + $unseenleft + ); } =head2 GetSoonestRenewDate --- a/C4/ILSDI/Services.pm +++ a/C4/ILSDI/Services.pm @@ -655,7 +655,7 @@ sub RenewLoan { # Add renewal if possible my @renewal = CanBookBeRenewed( $borrowernumber, $itemnumber ); - if ( $renewal[0] ) { AddRenewal( $borrowernumber, $itemnumber ); } + if ( $renewal[0] ) { AddRenewal( $borrowernumber, $itemnumber, undef, undef, undef, 0 ); } my $issue = $item->checkout; return unless $issue; # FIXME should be handled --- a/C4/SIP/ILS/Transaction/Renew.pm +++ a/C4/SIP/ILS/Transaction/Renew.pm @@ -51,6 +51,7 @@ sub do_renew_for { } else { $renewerror=~s/on_reserve/Item unavailable due to outstanding holds/; $renewerror=~s/too_many/Item has reached maximum renewals/; + $renewerror=~s/too_unseen/Item has reached maximum consecutive renewals without being seen/; $renewerror=~s/item_denied_renewal/Item renewal is not allowed/; $self->screen_msg($renewerror); $self->renewal_ok(0); --- a/Koha/CirculationRules.pm +++ a/Koha/CirculationRules.pm @@ -148,6 +148,9 @@ our $RULE_KINDS = { renewalsallowed => { scope => [ 'branchcode', 'categorycode', 'itemtype' ], }, + unseen_renewals_allowed => { + scope => [ 'branchcode', 'categorycode', 'itemtype' ], + }, rentaldiscount => { scope => [ 'branchcode', 'categorycode', 'itemtype' ], }, --- a/Koha/REST/V1/Checkouts.pm +++ a/Koha/REST/V1/Checkouts.pm @@ -146,6 +146,7 @@ sub renew { my $c = shift->openapi->valid_input or return; my $checkout_id = $c->validation->param('checkout_id'); + my $seen = $c->validation->param('seen') || 1; my $checkout = Koha::Checkouts->find( $checkout_id ); unless ($checkout) { @@ -169,7 +170,14 @@ sub renew { ); } - AddRenewal($borrowernumber, $itemnumber, $checkout->branchcode); + AddRenewal( + $borrowernumber, + $itemnumber, + $checkout->branchcode, + undef, + undef, + $seen + ); $checkout = Koha::Checkouts->find($checkout_id); $c->res->headers->location( $c->req->url->to_string ); @@ -223,6 +231,7 @@ sub allows_renewal { allows_renewal => $renewable, max_renewals => $rule->rule_value, current_renewals => $checkout->renewals, + unseen_renewals => $checkout->unseen_renewals, error => $error } ); --- a/api/v1/swagger/definitions/checkout.json +++ a/api/v1/swagger/definitions/checkout.json @@ -35,6 +35,10 @@ "type": ["integer", "null"], "description": "Number of renewals" }, + "unseen_renewals": { + "type": ["integer", "null"], + "description": "Number of consecutive unseen renewals" + }, "auto_renew": { "type": "boolean", "description": "Auto renewal" --- a/api/v1/swagger/parameters.json +++ a/api/v1/swagger/parameters.json @@ -38,6 +38,9 @@ "checkout_id_pp": { "$ref": "parameters/checkout.json#/checkout_id_pp" }, + "seen_pp": { + "$ref": "parameters/checkout.json#/seen_pp" + }, "match": { "name": "_match", "in": "query", --- a/api/v1/swagger/parameters/checkout.json +++ a/api/v1/swagger/parameters/checkout.json @@ -5,5 +5,12 @@ "description": "Internal checkout identifier", "required": true, "type": "integer" + }, + "seen_pp": { + "name": "seen", + "in": "query", + "description": "Item was seen flag", + "required": false, + "type": "integer" } } --- a/api/v1/swagger/paths/checkouts.json +++ a/api/v1/swagger/paths/checkouts.json @@ -81,9 +81,10 @@ "x-mojo-to": "Checkouts#renew", "operationId": "renewCheckout", "tags": ["patrons", "checkouts"], - "parameters": [{ - "$ref": "../parameters.json#/checkout_id_pp" - }], + "parameters": [ + { "$ref": "../parameters.json#/checkout_id_pp" }, + { "$ref": "../parameters.json#/seen_pp" } + ], "produces": ["application/json"], "responses": { "201": { --- a/circ/renew.pl +++ a/circ/renew.pl @@ -43,6 +43,7 @@ my ( $template, $librarian, $cookie, $flags ) = get_template_and_user( my $schema = Koha::Database->new()->schema(); my $barcode = $cgi->param('barcode'); +my $unseen = $cgi->param('unseen') || 0; $barcode =~ s/^\s*|\s*$//g; # remove leading/trailing whitespae $barcode = barcodedecode($barcode) if( $barcode && C4::Context->preference('itemBarcodeInputFilter')); my $override_limit = $cgi->param('override_limit'); @@ -102,7 +103,15 @@ if ($barcode) { if ( C4::Context->preference('SpecifyDueDate') && $hard_due_date ) { $date_due = dt_from_string( $hard_due_date ); } - $date_due = AddRenewal( undef, $item->itemnumber(), $branchcode, $date_due ); + $date_due = AddRenewal( + undef, + $item->itemnumber(), + $branchcode, + $date_due, + undef, + undef, + !$unseen + ); $template->param( date_due => $date_due ); } } --- a/koha-tmpl/intranet-tmpl/prog/css/src/staff-global.scss +++ a/koha-tmpl/intranet-tmpl/prog/css/src/staff-global.scss @@ -1025,6 +1025,10 @@ div { width: auto; } + .renew_formfield { + margin-bottom: 1em; + } + .circmessage { margin-bottom: .3em; padding: 0 .4em .4em; @@ -2481,6 +2485,14 @@ li { position: relative; } +#renew_as_unseen_label { + margin-left: 1em; +} + +#renew_as_unseen_checkbox { + margin-right: 1em; +} + #clearscreen { position: absolute; right: 0; --- a/koha-tmpl/intranet-tmpl/prog/en/includes/checkouts-table.inc +++ a/koha-tmpl/intranet-tmpl/prog/en/includes/checkouts-table.inc @@ -48,6 +48,10 @@ [% END %] [% END %] [% IF ( CAN_user_circulate_circulate_remaining_permissions ) %] + [% IF Koha.Preference( 'UnseenRenewals' ) %] + + + [% END %] [% END %] --- a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt @@ -1063,6 +1063,7 @@ var logged_in_user_borrowernumber = "[% logged_in_user.borrowernumber | html %]"; var ClaimReturnedLostValue = "[% Koha.Preference('ClaimReturnedLostValue') | html %]"; var ClaimReturnedChargeFee = "[% Koha.Preference('ClaimReturnedChargeFee') | html %]"; + var UnseenRenewals = "[% Koha.Preference('UnseenRenewals') | html %]"; var ClaimReturnedWarningThreshold = "[% Koha.Preference('ClaimReturnedWarningThreshold') | html %]"; var interface = "[% interface | html %]"; var theme = "[% theme | html %]"; --- a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/renew.tt +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/renew.tt @@ -52,6 +52,18 @@ [% END %] + [% ELSIF error == "too_unseen" %] + +

[% INCLUDE 'biblio-title.inc' biblio=item.biblio %] ( [% item.barcode | html %] ) has been renewed the maximum number of consecutive times without being seen by the library )

+ + [% IF Koha.Preference('AllowRenewalLimitOverride') %] +
+ + + +
+ [% END %] + [% ELSIF error == "too_soon" %]

[% INCLUDE 'biblio-title.inc' biblio=item.biblio %] ( [% item.barcode | html %] ) cannot be renewed before [% soonestrenewdate | $KohaDates %].

@@ -169,12 +181,25 @@

Renew

-
- -
- - - + [% IF Koha.Preference('UnseenRenewals') %] +
+
+ +
+ +
+
+ + +
+ [% ELSE %] +
+ +
+ + + + [% END %]
@@ -184,7 +209,6 @@
-
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember.tt +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember.tt @@ -924,6 +924,7 @@ var ClaimReturnedLostValue = "[% Koha.Preference('ClaimReturnedLostValue') | html %]"; var ClaimReturnedChargeFee = "[% Koha.Preference('ClaimReturnedChargeFee') | html %]"; var ClaimReturnedWarningThreshold = "[% Koha.Preference('ClaimReturnedWarningThreshold') | html %]"; + var UnseenRenewals = "[% Koha.Preference('UnseenRenewals') | html %]"; var interface = "[% interface | html %]"; var theme = "[% theme | html %]"; var borrowernumber = "[% patron.borrowernumber | html %]"; --- a/koha-tmpl/intranet-tmpl/prog/js/checkouts.js +++ a/koha-tmpl/intranet-tmpl/prog/js/checkouts.js @@ -152,9 +152,15 @@ $(document).ready(function() { itemnumber: itemnumber, borrowernumber: borrowernumber, branchcode: branchcode, - override_limit: override_limit, + override_limit: override_limit }; + if (UnseenRenewals) { + var ren = $("#renew_as_unseen_checkbox"); + var renew_unseen = ren.length > 0 && ren.is(':checked') ? 1 : 0; + params.seen = renew_unseen === 1 ? 0 : 1; + } + // Determine which due date we need to use var dueDate = isOnReserve ? $("#newonholdduedate input").val() : @@ -177,6 +183,8 @@ $(document).ready(function() { content += __("not checked out"); } else if ( data.error == "too_many" ) { content += __("too many renewals"); + } else if ( data.error == "too_unseen" ) { + content += __("too many consecutive renewals without being seen by the library"); } else if ( data.error == "on_reserve" ) { content += __("on hold"); } else if ( data.error == "restriction" ) { @@ -440,6 +448,13 @@ $(document).ready(function() { + __("Not renewable") + ""; + span_style = "display: none"; + span_class = "renewals-allowed"; + } else if ( oObj.can_renew_error == "too_unseen" ) { + msg += "" + + __("Must be renewed at the library") + + ""; + span_style = "display: none"; span_class = "renewals-allowed"; } else if ( oObj.can_renew_error == "restriction" ) { @@ -536,8 +551,11 @@ $(document).ready(function() { } content += msg; if ( can_renew || can_force_renew ) { - content += "(" - + __("%s of %s renewals remaining").format(oObj.renewals_remaining, oObj.renewals_allowed) + content += "("; + content + __("%s of %s renewals remaining").format(oObj.renewals_remaining, oObj.renewals_allowed); + if (UnseenRenewals && oObj.unseen_allowed) { + content += __(" / %s of %s unseen renewals remaining").format(oObj.unseen_remaining, oObj.unseen_allowed); + } + ")"; } --- a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-user.tt +++ a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-user.tt @@ -95,6 +95,8 @@
  • Your account has expired. Please contact the library for more information.
  • [% ELSIF error == 'too_many' %]
  • You have renewed this item the maximum number of times allowed.
  • + [% ELSIF error == 'too_unseen' %] +
  • You have renewed this item the maximum number of consecutive times without it being seen by the library.
  • [% ELSIF error == 'too_soon' %]
  • It is too soon after the checkout date for this item to be renewed.
  • [% ELSIF error == 'on_reserve' %] @@ -364,7 +366,12 @@ [% IF ISSUE.itemtype_object.rentalcharge_hourly > 0 %] [% ISSUE.itemtype_object.rentalcharge_hourly | $Price %] per hour [% END %] - ([% ISSUE.renewsleft | html %] of [% ISSUE.renewsallowed | html %] renewals remaining) + ( + [% ISSUE.renewsleft | html %] of [% ISSUE.renewsallowed | html %] renewals remaining + [% IF Koha.Preference('UnseenRenewals') && ISSUE.unseenallowed %] + / [% ISSUE.unseenleft | html %] of [% ISSUE.unseenallowed | html %] renewals left before the item must be seen by the library + [% END %] + ) [% ELSIF ( ISSUE.on_reserve ) %] Not renewable (on hold) [% ELSIF ( ISSUE.too_many ) %] @@ -375,16 +382,36 @@ No longer renewable [% ELSIF ISSUE.auto_too_much_oweing %] Automatic renewal failed, you have unpaid fines. - ([% ISSUE.renewsleft | html %] of [% ISSUE.renewsallowed | html %] renewals remaining) + ( + [% ISSUE.renewsleft | html %] of [% ISSUE.renewsallowed | html %] renewals remaining + [% IF Koha.Preference('UnseenRenewals') && ISSUE.unseenallowed %] + / [% ISSUE.unseenleft | html %] of [% ISSUE.unseenallowed | html %] renewals left before the item must be seen by the library + [% END %] + ) [% ELSIF ISSUE.auto_account_expired %] Automatic renewal failed, your account is expired. - ([% ISSUE.renewsleft | html %] of [% ISSUE.renewsallowed | html %] renewals remaining) + ( + [% ISSUE.renewsleft | html %] of [% ISSUE.renewsallowed | html %] renewals remaining + [% IF Koha.Preference('UnseenRenewals') && ISSUE.unseenallowed %] + / [% ISSUE.unseenleft | html %] of [% ISSUE.unseenallowed | html %] renewals left before the item must be seen by the library + [% END %] + ) [% ELSIF ( ISSUE.too_soon ) %] No renewal before [% ISSUE.soonestrenewdate | html %] - ([% ISSUE.renewsleft | html %] of [% ISSUE.renewsallowed | html %] renewals remaining) + ( + [% ISSUE.renewsleft | html %] of [% ISSUE.renewsallowed | html %] renewals remaining + [% IF Koha.Preference('UnseenRenewals') && ISSUE.unseenallowed %] + / [% ISSUE.unseenleft | html %] of [% ISSUE.unseenallowed | html %] renewals left before the item must be seen by the library + [% END %] + ) [% ELSIF ( ISSUE.auto_renew || ISSUE.auto_too_soon ) %] Automatic renewal - ([% ISSUE.renewsleft | html %] of [% ISSUE.renewsallowed | html %] renewals remaining) + ( + [% ISSUE.renewsleft | html %] of [% ISSUE.renewsallowed | html %] renewals remaining + [% IF Koha.Preference('UnseenRenewals') && ISSUE.unseenallowed %] + / [% ISSUE.unseenleft | html %] of [% ISSUE.unseenallowed | html %] renewals left before the item must be seen by the library + [% END %] + ) [% ELSIF ( ISSUE.item_denied_renewal ) %] Renewal not allowed [% END %] @@ -639,7 +666,12 @@ [% IF ( canrenew ) %] Renew [% END %] - ([% OVERDUE.renewsleft | html %] of [% OVERDUE.renewsallowed | html %] renewals remaining) + ( + [% OVERDUE.renewsleft | html %] of [% OVERDUE.renewsallowed | html %] renewals remaining + [% IF Koha.Preference('UnseenRenewals') && ISSUE.unseenallowed %] + / [% OVERDUE.unseenleft | html %] of [% OVERDUE.unseenallowed | html %] renewals left before the item must be seen by the library + [% END %] + ) [% ELSIF ( OVERDUE.norenew_overdue ) %] Not allowed(overdue) [% ELSIF ( OVERDUE.onreserve ) %] --- a/misc/cronjobs/automatic_renewals.pl +++ a/misc/cronjobs/automatic_renewals.pl @@ -95,7 +95,7 @@ while ( my $auto_renew = $auto_renews->next ) { $auto_renew->issue_id, $auto_renew->borrowernumber, $auto_renew->itemnumber; } if ($confirm){ - my $date_due = AddRenewal( $auto_renew->borrowernumber, $auto_renew->itemnumber, $auto_renew->branchcode ); + my $date_due = AddRenewal( $auto_renew->borrowernumber, $auto_renew->itemnumber, $auto_renew->branchcode, undef, undef, 0 ); $auto_renew->auto_renew_error(undef)->store; } push @{ $report{ $auto_renew->borrowernumber } }, $auto_renew; --- a/offline_circ/download.pl +++ a/offline_circ/download.pl @@ -67,6 +67,7 @@ my $issues_query = q{SELECT issues.date_due AS date_due, issues.issuedate AS issuedate, issues.renewals AS renewals, + issues.unseen_renewals AS unseen_renewals, borrowers.cardnumber AS cardnumber, CONCAT(borrowers.surname, ', ', borrowers.firstname) AS borrower_name FROM issues --- a/opac/opac-renew.pl +++ a/opac/opac-renew.pl @@ -62,7 +62,7 @@ else { my ( $status, $error ) = CanBookBeRenewed( $borrowernumber, $itemnumber ); if ( $status == 1 && $opacrenew == 1 ) { - AddRenewal( $borrowernumber, $itemnumber, undef, undef, undef ); + AddRenewal( $borrowernumber, $itemnumber, undef, undef, undef, undef, 0 ); push( @renewed, $itemnumber ); } else { --- a/opac/opac-user.pl +++ a/opac/opac-user.pl @@ -223,7 +223,14 @@ if ( $pending_checkouts->count ) { # Useless test # check if item is renewable my ($status,$renewerror) = CanBookBeRenewed( $borrowernumber, $issue->{'itemnumber'} ); - ($issue->{'renewcount'},$issue->{'renewsallowed'},$issue->{'renewsleft'}) = GetRenewCount($borrowernumber, $issue->{'itemnumber'}); + ( + $issue->{'renewcount'}, + $issue->{'renewsallowed'}, + $issue->{'renewsleft'}, + $issue->{'unseencount'}, + $issue->{'unseenallowed'}, + $issue->{'unseenleft'} + ) = GetRenewCount($borrowernumber, $issue->{'itemnumber'}); ( $issue->{'renewalfee'}, $issue->{'renewalitemtype'} ) = GetIssuingCharges( $issue->{'itemnumber'}, $borrowernumber ); $issue->{itemtype_object} = Koha::ItemTypes->find( Koha::Items->find( $issue->{itemnumber} )->effective_itemtype ); if($status && C4::Context->preference("OpacRenewalAllowed")){ @@ -235,6 +242,7 @@ if ( $pending_checkouts->count ) { # Useless test if ($renewerror) { $issue->{'too_many'} = 1 if $renewerror eq 'too_many'; + $issue->{'too_unseen'} = 1 if $renewerror eq 'too_unseen'; $issue->{'on_reserve'} = 1 if $renewerror eq 'on_reserve'; $issue->{'norenew_overdue'} = 1 if $renewerror eq 'overdue'; $issue->{'auto_renew'} = 1 if $renewerror eq 'auto_renew'; --- a/svc/checkouts +++ a/svc/checkouts @@ -162,7 +162,14 @@ while ( my $c = $sth->fetchrow_hashref() ) { ) : undef; - my ( $renewals_count, $renewals_allowed, $renewals_remaining ) = + my ( + $renewals_count, + $renewals_allowed, + $renewals_remaining, + $unseen_count, + $unseen_allowed, + $unseen_remaining + ) = GetRenewCount( $c->{borrowernumber}, $c->{itemnumber} ); my ( $itemtype, $recordtype, $type_for_stat ); @@ -240,6 +247,9 @@ while ( my $c = $sth->fetchrow_hashref() ) { renewals_count => $renewals_count, renewals_allowed => $renewals_allowed, renewals_remaining => $renewals_remaining, + unseen_count => $unseen_count, + unseen_allowed => $unseen_allowed, + unseen_remaining => $unseen_remaining, return_claim_id => $c->{return_claim_id}, return_claim_notes => $c->{return_claim_notes}, --- a/svc/renew +++ a/svc/renew @@ -46,6 +46,7 @@ my $borrowernumber = $input->param('borrowernumber'); my $override_limit = $input->param('override_limit'); my $branchcode = $input->param('branchcode') || C4::Context->userenv->{'branch'}; +my $seen = $input->param('seen'); my $date_due; if ( $input->param('date_due') ) { $date_due = dt_from_string( scalar $input->param('date_due') ); @@ -66,7 +67,7 @@ if ( $data->{error} && $data->{error} eq 'on_reserve' && C4::Context->preference } if ( $data->{renew_okay} ) { - $date_due = AddRenewal( $borrowernumber, $itemnumber, $branchcode, $date_due ); + $date_due = AddRenewal( $borrowernumber, $itemnumber, $branchcode, $date_due, undef, $seen ); $data->{date_due} = output_pref( { dt => $date_due, as_due_date => 1 } ); } --